Haskell如何推断(+)。(+)的类型

zic*_*liu 5 haskell type-inference

为什么类型(+).(+)IS (Num a, Num (a -> a)) => a -> (a -> a) -> a -> a

(+) :: Num a => a -> a -> a
(.) :: (b -> c) -> (a -> b) -> a -> c
Run Code Online (Sandbox Code Playgroud)

我曾尝试过,但不知道结果如何(Num a, Num (a -> a)) => a -> (a -> a) -> a -> a

(+) :: Num i => i -> i -> i
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(+) :: Num j => j -> j -> j

(+) . (+) :: (i -> (i -> i)) -> ((j -> j) -> i) -> ((j -> j) -> (i -> i))
where: 
  b = i
  c = i -> i
  a = j -> j
Run Code Online (Sandbox Code Playgroud)

有人可以帮忙给我分析步骤吗?

luq*_*qui 6

另一种方法:

((+).(+)) x y z
= (+) ((+) x) y z
= ((x +) + y) z
Run Code Online (Sandbox Code Playgroud)

现在注意:

(x +) + y
Run Code Online (Sandbox Code Playgroud)

是两个函数的和,因为自变量之一是一个函数。让我们假设,x :: a因为我们必须从某个地方开始(这​​个假设不一定意味着a将出现在最终类型中,我们只是想给它起一个名字就可以开始我们的推理过程)

x :: a
Run Code Online (Sandbox Code Playgroud)

然后

(x +) :: a -> a
y :: a -> a      -- since the operands of + have to be the same type
Run Code Online (Sandbox Code Playgroud)

此外,我们正在增加x(x +)这给了我们Num a,我们也加入这给我们的功能Num (a -> a) (在这一点上,这基本上是一个错误,因为没有理智的人定义的一个实例Num的功能-这是可能的,但它是一个坏主意) 。

无论如何,我们刚刚分析的表达式是type的两个事物的总和a -> a,因此结果也必须是type a -> a

((x +) + y) z    -- since the result of + has to have the same type 
^^^^^^^^^^^      --                                    as its operands 
  a -> a
Run Code Online (Sandbox Code Playgroud)

因此,z :: a

所以最后整个表达式都被输入了

(+).(+) :: a -> (a -> a) -> a -> a
           ^    ^^^^^^^^    ^    ^
           x       y        z    final result
Run Code Online (Sandbox Code Playgroud)

加上我们在此过程中遇到的限制。


Rob*_*ond 6

我的解释中倾向于使用更多的单词,而使用的公式则更少-如果这没有帮助,则表示歉意,但是其他答案让我有些不满意,因此我认为我的解释方式会稍有不同。

显然,您已经对各个部分的功能有所了解。特别是(.)需要两个函数,并给出一个函数,该函数首先将其右边的函数应用于参数,然后将其左边的函数应用于结果。

因此,我们将从右侧的功能开始:(+)。它具有类型(Num a) => a -> a -> a-也就是说,它接受两个参数,这些参数必须是相同的类型,并且该类型必须是的实例Num。然后,它返回相同类型的值。

回想一下,由于curring,它等效于(Num a) => a -> (a -> a)。也就是说,它接受的实例Num,然后将相同类型的函数返回给自身。

现在,我们必须使用另一种函数来组合它-该函数以其结果类型作为输入。那其他功能是什么?这是(+)又来了!但是我们知道,为了对组合进行类型检查,我们必须向其提供一个函数类型:(a -> a其中a是的一个实例Num)。正如我们已经看到的那样,这不是问题,因为(+)可以接受作为实例的任何类型Numa -> a当我们有适当的实例时,可以包括。这就解释了为什么最终类型具有2个约束Num aNum (a -> a)

现在很容易将所有内容放在一起。摆脱约束(我们已经处理过),我们示意性地具有:

(+)                                 .     (+)
(a -> a) -> ((a -> a) -> (a -> a))        a -> (a -> a)
Run Code Online (Sandbox Code Playgroud)

因此,在(.)我将写为的类型签名中,(c -> d) -> (b -> c) -> (b -> d)我们有aas ba -> aas c(a -> a) -> (a -> a)as d。代入这些,我们得到的最终类型(b -> d)为:

a -> ((a -> a) -> (a -> a))
Run Code Online (Sandbox Code Playgroud)

我们可以重写为

a -> (a -> a) -> a -> a
Run Code Online (Sandbox Code Playgroud)

在回忆起功能箭头是右相关的之后,通过删除不必要的括号。