我正在自学Haskell,而且我遇到了两个“翻转”功能的实现,它为我提出了关于名称声明的问题。
这两个做同样的事情:
flip'' :: (a -> b -> c) -> b -> a -> c
flip'' f y x = f x y
flip' :: (a -> b -> c) -> (b -> a -> c)
flip' f = g
where g x y = f y x
第一个例子正如我所料。在第二个例子中,当我们还没有声明x或y时,我很困惑为什么我们被允许写g x y = f y x
。我理解懒惰的评估意味着在需要之前都不会进行评估,但我希望编译器至少需要声明。
即使没有类型签名,它也会编译......这样可以正常工作:
flip' f = g
where g x y = f y x
x和y只是完全无类型的变量吗?或者还有其他事情发生了吗?为什么我们能够做到这一点?
答案 0 :(得分:3)
where
与声明一个全新的函数完全相同。唯一的区别是函数存在于使用where的函数的局部范围内。这意味着您在调用时提供的代码等同于:
flip' f -- Equivalent to flip' (\x y -> f x y)
-- After we call it we have in our environment(scope) function f
g x y = f y x -- Here you call it and it is perfectly fine, because f is defined
类型怎么样?编译器会尝试根据g
的信息推断出g
的类型。它具有的信息是f
的类型必须与f :: a -> b -> c
相同,除了翻转参数的顺序,它还注意到f只有两个参数。因此,类型推导算法一般表示如果g :: b -> a -> c
则{{1}}。
答案 1 :(得分:3)
我很困惑,为什么我们不允许写g x y = f y x 声明x或y。
由于g,x和y出现在等号的左边,所以它们实际上是声明的。 where
为其附加的代码引入了本地范围。代码可以写成:
flip f = let g x y = f y x in g
英语:设g是一个带有两个参数的函数,叫做x和y ....
答案 2 :(得分:2)
第二个例子没有什么特别之处。同样在第一个示例中,类型签名是可选的,因此以下行自行运行:
flip'' f y x = f x y
在Haskell中,您不必指定变量的类型,但Haskell实现可以为您推断它们。您可以在ghci中使用:t flip''
来检查flip''
的推断类型是否符合预期。
那么你可以在Haskell中使用没有声明或没有类型的变量吗?不可以。例如,您只能使用绑定在某处的变量,因为它们出现在等式中=
符号的左侧。并且每个变量都必须具有类型,即使它是问题中的a
,b
和c
等多态类型变量。只是程序员不必向Haskell实现声明类型,但Haskell实现可以根据变量的使用方式来计算类型。
请注意,这种“计算输出”业务仍然是静态输入。 Haskell实现首先计算出所有变量的类型,然后编译或运行程序。因此,如果存在类型错误,则在程序开始运行之前会收到错误消息。