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

问题描述 投票:5回答:6

为什么(+).(+)的类型是(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

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

haskell type-inference
6个回答
6
投票

我的解释往往会有更多的单词和更少的公式 - 如果这没有帮助就道歉,但我对其他答案有点不满意,所以我想我会以稍微不同的方式解释。

很明显,您已经清楚了解各个部件的功能。特别是(.)有两个函数,并给出首先将函数应用到参数右侧的函数,然后将函数左侧的函数应用于结果。

所以我们将从右边的函数开始:(+)。这有类型(Num a) => a -> a -> a - 也就是说,它需要两个必须是相同类型的参数,并且该类型必须是Num的一个实例。然后它返回相同类型的值。

回想一下,由于currying,这相当于(Num a) => a -> (a -> a)。也就是说,它需要一个Num实例,并从同一类型返回一个函数给自己。

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

现在把所有东西放在一起很容易。放弃约束(我们已经处理过),我们示意地:

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

所以在(.)的类型签名中,我将其写为(c -> d) -> (b -> c) -> (b -> d),我们将a称为b,将a -> a称为c,将(a -> a) -> (a -> a)称为d。用这些代替,我们得到最终类型(b -> d):

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

我们可以改写为

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

通过删除不必要的括号,在调用函数箭头是右关联之后。


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

加上我们沿途拾取的限制因素。


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 )

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


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))

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


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
© www.soinside.com 2019 - 2024. All rights reserved.