为什么(+).(+)
的类型是(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
有人可以帮忙并给我分析步骤吗?
我的解释往往会有更多的单词和更少的公式 - 如果这没有帮助就道歉,但我对其他答案有点不满意,所以我想我会以稍微不同的方式解释。
很明显,您已经清楚了解各个部件的功能。特别是(.)
有两个函数,并给出首先将函数应用到参数右侧的函数,然后将函数左侧的函数应用于结果。
所以我们将从右边的函数开始:(+)
。这有类型(Num a) => a -> a -> a
- 也就是说,它需要两个必须是相同类型的参数,并且该类型必须是Num
的一个实例。然后它返回相同类型的值。
回想一下,由于currying,这相当于(Num a) => a -> (a -> a)
。也就是说,它需要一个Num
实例,并从同一类型返回一个函数给自己。
现在我们必须用另一个函数组合它 - 一个将结果类型作为输入的函数。那是什么其他功能?再次是(+)
!但是我们知道,为了对组合进行类型检查,我们必须为它提供一个函数类型:a -> a
(其中a
是Num
的一个实例)。正如我们已经看到的,这不是问题,因为(+)
可以采用任何类型的Num
实例。当我们有适当的实例时,这可以包括a -> a
。这就解释了为什么最终类型有2个约束Num a
和Num (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
通过删除不必要的括号,在调用函数箭头是右关联之后。
一种不同的方法:
((+).(+)) 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
加上我们沿途拾取的限制因素。
你快到了。
(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 )
(.) :: (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
类型推导是纯粹的机械过程:
(+) :: 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))
(+) . (+)
与(.) (+) (+)
相同。
(.)
和(+)
的类型是这样的:
(+) :: 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