【发布时间】:2016-05-16 12:08:28
【问题描述】:
我开始学习 Haskell。在学习教程时,我发现以下示例可以在算术表达式中使用函数:
module FunNat where
instance Num a => Num (t -> a) where
(+) = fun2 (+)
(*) = fun2 (*)
(-) = fun2 (-)
abs = fun1 abs
signum = fun1 signum
fromInteger = const . fromInteger
fun1 :: (a -> b) -> ((t -> a) -> (t -> b))
fun1 = (.)
fun2 :: (a -> b -> c) -> ((t -> a) -> (t -> b) -> (t -> c))
fun2 op a b = \t -> a t `op` b t
该示例有效。但我无法理解(+) 函数是如何转换为两个参数的函数的。据我所知,每个(+) 都替换为fun2 (+)。 fun2 相当于一个参数的函数\t -> a t 'op' b t,但我们应该有一个两个参数的函数(类似于(\t1 t2 -> (\x -> x ) t1 + (\x -> x) t2))。我认为这里应该应用一些 Haskell 类型的基本概念,但我不知道它们是什么。
编辑 1
我知道fun2 是三个参数的函数。我无法理解内部表达转换。我的推理方式如下:(+) 3 4 = (+) (\x->3) (\x->4) = fun2 (+) (\x->3) (\x->4) = \t -> (\x->3) t + (\x->4) t
这是什么t?或者我的推理哪里错了?是否有必要换一种方式思考?
编辑 2
我认为我已经对这个问题有所了解(谢谢大家!)。所以:
当我们编写
(+) 3 4时 - 在这种情况下,使用来自Num的简单操作,没有来自FunNat的特殊功能。要从FunNat使用(+),必须写成fun2 (+) 3 4或fun2 (+) (\x -> 3) (\x -> 4)。但是这些表达式要求对任何第三个参数进行评估;为了演示
FunNat的特定功能,我们可以使用以下示例((*)-(+))(取自教程)或其他形式 -(-) (*) (+)。两个表达式都有两个参数。在这种情况下,我们有:((*)-(+))=fun2 (-) (*) (+)=\t -> (*) t - (+) t=\t -> (\t1 t2 -> t1 * t2) t - (\t1 t2 -> t1 + t2) t=(\t t2 -> t * t2) - (\t t2 -> t + t2)=\t t2 -> (t * t2) - (t + t2)。最后的表达式只需要Num。我希望所有这些都是正确的正如教程中解释的那样,但我不明白为什么,使用
(*)和(+)作为fun2的参数的可能性,期望(t->a)基于以下(取自教程):t1 -> t2 -> a=t1->a'其中a' = t2 -> a。这里使用了curring。
所以所有必要的事实都在表面上,但我没有考虑类型推断的重要机制。
【问题讨论】:
-
fun2 (+)根据定义等同于\a b t -> a t + b t。它实际上是 3 个参数的函数 - 它的类型是Num a => {-1-}(t -> a) -> {-2-}(t -> a) -> {-3-}t -> a -
感谢您的回答。我试图澄清问题。
-
@您的编辑。
t是 lambda 绑定的变量。(\x -> 3) t只是3所以\t -> 3 + 4是最终表达式。 -
但是为了计算
\t -> 3 + 4,我们需要传递任何t。我的意思是,如果我们输入let f = \t -> 3 + 4,那么为了得到我们需要的结果,例如输入 f 42。我的意思是,如果我们最终得到\t -> (\x->3) t + (\x->4) t表达式是正确的,我们需要一些额外的 any t 来评估它,即(+) 3 4 42。还是不行? -
(+) 3 4 = (+) (\x->3) (\x->4)-- 这大错特错。3的类型为Integer,但\x -> 3的类型为a -> Integer。这两者不相等。至于其余的: