Function Haskell中的算子与函数优先级
所以这不起作用:Function Haskell中的算子与函数优先级,function,haskell,operator-keyword,infix-notation,operator-precedence,Function,Haskell,Operator Keyword,Infix Notation,Operator Precedence,所以这不起作用: take 50 iterate (* 2) 1 因为take的第二个参数中需要括号。我的问题是为什么 Haskell正确地看到了类型上的差异: Couldn't match expected type `[a0]' with actual type (a1 -> a1) -> a1 -> [a1]' 我的问题是: 1) haskell似乎首先尝试在迭代函数之前解析take 50函数。哈斯克尔为什么这样做?在数学中,如果你有fgtw
take 50 iterate (* 2) 1
因为take的第二个参数中需要括号。我的问题是为什么
Haskell正确地看到了类型上的差异:
Couldn't match expected type `[a0]'
with actual type (a1 -> a1) -> a1 -> [a1]'
我的问题是:
1) haskell似乎首先尝试在迭代函数之前解析take 50函数。哈斯克尔为什么这样做?在数学中,如果你有fgtwu(x),你首先要计算u(x),然后再做其他事情。在这种情况下,为什么haskell从计算f开始 2) Haskell非常聪明,能够检测到实际的类型是:
(a1 -> a1) -> a1 -> [a1]
现在,如果它看到这个函数的输出是[a1],一个将与预期类型[a0]统一的类型,为什么haskell不统一它
3) 为什么$operator会解决这个问题?我知道:
($) :: (a -> b) -> a -> b
基本上,这个操作符的作用是说“write FUNCTION$ARGUMENT”,然后得到在该参数处计算的函数值。在take 50的情况下,类型为:
take 50 :: [a2]->[a2]
take 50 $ :: a->b
where a ~a2 and b~b2 then
take 50 $ :: [a2]->[a2]
因此,基本上我和第一种情况相同,没有使用括号或$。这个例子是我需要一个[a2]类型的参数(haskell称它为[a0],但它是一样的。那么..为什么haskell在我使用$时将[a2]与(a1->a1)->a1->[a1]统一起来,但在我不使用它时它却不统一呢?take 50 iterate(*2)1
在带有括号的语言中与take(50,iterate,*2,1)是等价的
,但您需要的是获取(50,iterate(*2,1))
。$
的目的正是“在这里打开一个括号,并尽可能地关闭它”。这就像调用一个函数一样,但是右关联而不是左关联。您可以使用获取50(iterate),在不使用$
操作符的情况下编写它(*2)1)
fgtwu(x)
在数学中也不是一系列函数应用程序。然而,在数学中,如果我们将
作为函数组合运算符,那么(f.g.t.w.u)(x)
首先应用u
函数,然后应用w
函数,依此类推。这在Haskell中也是一样的:(f.g.t.w.u)x
在您的第一个类型错误中,它试图将类型[a0]
与类型(a1->a1)->a1->[a1]
统一,而不是与类型[a1]
统一。这不可能统一,因此它给出了一个错误
并置是Haskell中的函数应用,而不是函数组合(顺便说一句,在数学中并置通常是乘法)。这意味着像fxyz
这样的表达式(本质上)意味着“将函数f
应用于参数x
,y
和z
”。这相当于((f x)y)z
。Haskell中的每个函数只接受一个参数。这意味着类似f x y z=x+y*z
的内容实际上与f=\x->\y->\z->x+y*z
相同。函数应用程序是左关联的,因此f x y z
与编写((f x)y)相同z
,即将f
应用于x
首先获取结果函数,并将其应用于y
。最后,将该函数应用于z
这意味着您的原始表达式被解释为(再次转换为标准数学符号):
take(50,iterate,(*2),1)
函数应用程序是左关联的,因此
f g a b
是
你有吗
(((take 50) iterate) (*2)) 1
这是不正确的,因为它将take 50::[a]->[a]
应用到iterate
,类型为(a->a)->a->[a]
,显然是不正确的。函数应用程序(由“并置”表示,将函数及其参数放在一起)必须以某种方式进行解析,并且它必须是左关联的或右关联的,才能进行明确的解析。1
函数应用程序实际上是左关联的,所以您编写的内容与
((((take 50) iterate) (* 2)) 1)
如果它是正确的,那么你会
(take (50 (iterate ((* 2) 1))))
这也不是你想要的,作为一个默认选择,情况更糟:很少有程序会让正确的关联操作符感觉更自然
由于没有统一的解析规则可以生成您想要的程序,因此您,程序员,有必要通过以$
或一些括号的形式给出提示来告诉haskell您的实际意思
至于为什么$
有帮助:它被定义为在解析中具有非常低的优先级,因此
take 50 $ iterate (* 2) 1
解析为
(take 50) $ ((iterate (* 2)) 1))
这正是你想要的
1明确的解析对于具有可理解的程序来说是一个非常理想的属性,而让typechecker决定如何解析(正如您所建议的那样)将是一件非常混乱的事情。(我会给你们一个更详细的答案,涵盖一些我认为其他答案没有提及的角落,至少不是明确的)
在Haskell中,括号并不表示函数调用。它们只是用于分组。因此,u(x)
与Haskell中的ux
完全相同
您第一次显示的类型错误是由Haskell试图使用iterate
作为take
的第二个参数生成的:
take :: Int -> [a0] -> [a0]
take 50 iterate :: t
iterate :: (a1 -> a1) -> a1 -> [a1]
------------------------
[](...) and (->)(...) do not match
它没有尝试“在解析take 50
函数后解析iterate
函数”。它还没有查看长表达式的其余部分。它只是尝试使用iterate
值。这恰好是一个函数
函数只是Haskell中的值,这就是为什么没有特殊的语法来调用它们,就像在各种foo(args){exps;};
语言中一样
对于编译器来说,试图为您更正表达式太危险了:太多的打字错误或错误会突然产生意想不到的含义。最多这可能是某个假设的交互式开发环境的一个特性
当你写时,拿50美元
take :: Int -> [a0] -> [a0]
take 50 iterate :: t
iterate :: (a1 -> a1) -> a1 -> [a1]
------------------------
[](...) and (->)(...) do not match
iterate :: (a1 -> a1) -> a1 -> [a1]
iterate (*2) 1 :: [Int] -- it's actually (Num a => [a])
-- but that's besides the point
take :: Int -> [a0] -> [a0]
take 50 (iterate (*2) 1 ) :: t
iterate (*2) 1 :: (Num a) => [a ]
------------------
a0 ~ (Num a)=> a t ~ (Num a) => [a]