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]