我想弄明白
(*) . (+)
在Haskell中。我知道复合算子只是数学函数的标准复合-所以
(f . g) = f (g x)
但是:
(*) . (+) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a
我很难理解这种类型的签名。我希望能够做这样的事情:
((*) . (+)) 1 2 :: Num a => a -> a
= (* (+ 1 2))
(*).(+)的类型签名是什么意思?我试着用类似这样的方式来处理它(只是匹配它的签名):
((*) . (+)) 1 (\x -> x + 1) 1
但是它无法编译,我试着在编写这些代码时遍历逻辑步骤,但是我不完全理解它是如何得到这个结果的(以及结果是什么)。
5条答案
按热度按时间mkh04yzy1#
我理解你的感受。一开始我也发现函数组合很难掌握。帮助我理解这个问题的是类型签名。考虑一下:
现在,当你写
(*) . (+)
时,它实际上与(.) (*) (+)
相同(即(*)
是(.)
的第一个参数,(+)
是(.)
的第二个参数):因此,
(*)
的类型签名(即Num x => x -> x -> x
)与b -> c
统一:因此,
(+)
的类型签名(即Num y => y -> y -> y
)与Num x => a -> x
统一:我希望这能澄清
Num (y -> y)
和Num y
从何而来。您将得到一个非常奇怪的(Num (y -> y), Num y) => y -> (y -> y) -> y -> y
类型的函数。奇怪的是,它期望
y
和y -> y
都是Num
的示例。y
应该是Num
的示例是可以理解的,但是y -> y
怎么可能是y -> y
的示例呢?让y -> y
成为Num
的示例似乎不合逻辑。这不可能是正确的。然而,当你看到函数组合实际上做了什么时,这是有意义的:
所以你有一个函数
\z -> (*) ((+) z)
,z
显然是Num
的一个示例,因为(+)
应用于它,因此\z -> (*) ((+) z)
的类型是Num t => t -> ...
,其中...
是(*) ((+) z)
的类型,我们马上就会知道。因此
((+) z)
是Num t => t -> t
类型,因为它需要多一个数,但是,在它应用于另一个数之前,(*)
被应用于它。因此,
(*)
期望((+) z)
是Num
的示例,这就是为什么期望t -> t
是Num
的示例。因此,...
被(t -> t) -> t -> t
替换,并且添加了约束Num (t -> t)
,从而得到类型(Num (t -> t), Num t) => t -> (t -> t) -> t -> t
。您真正想要合并
(*)
和(+)
的方法是使用(.:)
:因此
(*) .: (+)
与\x y -> (*) ((+) x y)
相同。现在给(+)
两个参数,以确保((+) x y)
确实只是Num t => t
而不是Num t => t -> t
。因此,
((*) .: (+)) 2 3 5
是(*) ((+) 2 3) 5
,这是(*) 5 5
,这是25
,我相信这是你想要的。请注意,
f .: g
也可以写成(f .) . g
,(.:)
也可以定义为(.:) = (.) . (.)
。您可以在这里阅读更多信息:What does (f .) . g mean in Haskell?
vwoqyblh2#
(*)
和(+)
都有类型签名Num a => a -> a -> a
现在,如果你组合它们,你会得到一些时髦的东西。这是因为
(*)
和(+)
需要两个“参数”。带一个参数的(+)得到一个函数,
.
运算符需要这个函数(如a -> a
所示)。以下是
(*) . (+)
的含义(*) . (+)
将x f y
Map到((x +) * f) y
,其中f
是从a
到a
的函数,它也是一个数字。(*)
需要函数的原因是在需要两个参数时使类型匹配,但该函数必须是数字,因为(*)
只对数字起作用。真的,这个函数根本没有意义。
oxalkeyp3#
首先是一些扩展:
如其他答案所示,您的函数是
但是这个函数的语义并不奇怪。
有difference lists的概念,相应地,也有差整数的概念,我见过它们只在依赖类型的设置中使用(例如here,但这不是唯一的情况),定义的相关部分是
这在Haskell中没有多大意义,但对于依赖类型可能很有用。
现在我们可以写
或者
在这两种情况下都是
fromEnum test == 12
。可以避免使用
TypeSynonymInstances
扩展:像以前一样我们可以写
但现在我们也可以写
还有
打印
12
。im9ewurl4#
假设:
那么
现在
m
需要一个Num a
作为参数,另一方面,(a x)
,也就是说,(x +)
是一个一元函数(a -> a)
,根据(+)
的定义,我猜发生的事情是GHC试图将这两种类型结合起来,这样,如果你有一个既是数字又是一元函数的类型,m
可以接受数字和一元函数,并返回一元函数,因为它们被视为相同的类型。正如@Syd指出的,这种统一对于任何普通的数字类型(如整数和浮点数)都没有意义。
n3h0vuf25#
这里有很好的答案,但让我快速指出几个步骤,你错了。
首先,函数组合的正确定义是
你忽略了LHS上的
x
。接下来,你应该记住在Haskell中h x y
与(h x) y
是相同的。所以,与你所期望的相反,现在你知道为什么失败了。而且
不起作用,因为不满足约束
Num (Int -> Int)
。