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

时间:2019-04-05 06:38:52

标签: haskell type-inference

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

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

我尝试过,但不知道如何得出(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

有人可以帮我分析一下步骤吗?

6 个答案:

答案 0 :(得分:6)

另一种方法:

((+).(+)) x y z
= (+) ((+) x) y z
= ((x +) + y) z

现在注意:

(x +) + y

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

x :: a

然后

(x +) :: a -> a
y :: a -> a      -- since the operands of + have to be the same type

此外,我们在x中添加(x +),这给了我们Num a,并且我们添加了给我们Num (a -> a)的函数(目前,这实际上是一个错误,因为没有理智的人为函数定义Num的实例-可能,但这是个坏主意。

无论如何,我们刚才分析的表达式是a -> a类型的两件事的总和,所以结果也必须是a -> a类型的。

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

因此,z :: a

所以最终整个表达式都被键入

(+).(+) :: a -> (a -> a) -> a -> a
           ^    ^^^^^^^^    ^    ^
           x       y        z    final result

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

答案 1 :(得分:6)

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

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

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

回想一下,由于存在咖喱,这等效于(Num a) => a -> (a -> a)。也就是说,它接受Num的实例,并从相同类型返回一个函数给它自己。

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

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

(+)                                 .     (+)
(a -> a) -> ((a -> a) -> (a -> a))        a -> (a -> a)

因此,在(.)的类型签名(我将其写为(c -> d) -> (b -> c) -> (b -> d)中,我们将a命名为b,将a -> a命名为{{1 }},而c(a -> a) -> (a -> a)。将它们代入后,我们得到的最终类型(d)为:

b  -> d

我们可以改写为

a -> ((a -> a) -> (a -> a))

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

答案 2 :(得分:2)

您快到了。

(Num i =>)
b = i
c = i -> i
(Num j =>)
a = j -- this is different, you associated b,c correctly but a wrongly
b = j -> j
=> i = j -> j -- ( by i = b = j -> j )
=> c = (j -> j) -> (j -> j) -- ( by c = i -> i, i = j -> j )
=> (+) . (+) :: a -> c = j -> ((j -> j) -> (j -> j)) 
    = j -> (j -> j) -> j -> j -- ( simplifying brackets )

答案 3 :(得分:2)

(.) :: (b -> c) -> (a -> b) -> a -> c

If you apply two functions to it you get in pseudocode:
(+) . (+) -> a -> c

So what is the type of a? It has the same type as the first argument 
of the second function you pass into (.). You can rewrite the type 
of (+) with brackets and get
(+) :: a -> (a -> a)

From that we know that

a :: a
b :: (a -> a)

Now that we know what a and b are, let's look at the first argument of (.).

It's (+) applied by an argument b :: (a -> a). So we get
(a -> a) -> ((a -> a) -> (a -> a))

so we found the type of c:
c :: (a -> a) -> (a -> a) 
and since we can remove the second pair of brackets
c :: (a -> a) -> a -> a

if we plug our findings into 

(+) . (+) -> a -> c

we get 

(+) . (+) :: a -> (a -> a) -> a -> a

答案 4 :(得分:1)

类型派生是纯机械过程:

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

(.)    ((+) :: t -> (t->t))     ((+) :: s -> (s->s))  :: a -> c
----------------------------------------------------
            b ~ t, c ~ t->t,          a ~ s, b ~ s->s    s -> (t -> t)
            Num t                     Num s              s -> (b -> b)
            Num b                     Num s              s -> ((s->s) -> (s->s))
            Num (s->s)                Num s              s -> ((s->s) -> (s->s))

(+) . (+)(.) (+) (+)相同。

答案 5 :(得分:0)

(.)(+)的类型如下:

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

我们可以重新贴标签以避免混淆:

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

现在,让我们算出(+) (.)的类型。这是(.),并且应用了一个参数,因此结果将是(a -> b) -> a -> c,并且我们已经将(b -> c)与第一个(+)的类型进行了匹配,因此:

(b -> c) = Num i => i -> i -> i
b = i
c = i -> i

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

现在,我们可以对此应用第二个参数。结果将是a -> i -> i,并且我们已经将(a -> i)与第二个(+)的类型进行匹配,因此:

(a -> i) = Num j => j -> j -> j
a = j
i = j -> j

(+) (.) (+) :: Num i => a -> i -> i
= (Num i, Num j) => j -> i -> i -- replacing a
= (Num (j -> j), Num j) => j -> (j -> j) -> (j -> j) -- replacing i
= (Num (j -> j), Num j) => j -> (j -> j) -> j -> j -- simplifying