首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >当我和+在Haskell作曲时发生了什么?

当我和+在Haskell作曲时发生了什么?
EN

Stack Overflow用户
提问于 2014-12-27 04:02:00
回答 5查看 1.7K关注 0票数 45

我在试着理解

代码语言:javascript
复制
(*) . (+) 

在哈斯克尔。我知道合成算子只是数学函数的标准组合-所以

代码语言:javascript
复制
(f . g) = f (g x)

但是:

代码语言:javascript
复制
(*) . (+) :: (Num (a -> a), Num a) => a -> (a -> a) -> a -> a

我很难理解这种签名。我本来希望能做这样的事情:

代码语言:javascript
复制
((*) . (+)) 1 2 :: Num a => a -> a
= (* (+ 1 2))

(*)的意思是什么?(+)类型签名?我试着用这样的方法来玩它(只是与它的签名相匹配):

代码语言:javascript
复制
((*) . (+)) 1 (\x -> x + 1) 1

但这不能进行编译。在编写这些内容时,我试图遍历逻辑步骤,但我并不完全理解它是如何达到这个结果的(以及结果是什么)。

EN

Stack Overflow用户

回答已采纳

发布于 2014-12-27 04:57:36

我理解你的感受。一开始,我发现函数构图也很难掌握。帮助我摸索这件事的是打字签名。考虑:

代码语言:javascript
复制
(*) :: Num x => x -> x -> x
(+) :: Num y => y -> y -> y
(.) :: (b -> c) -> (a -> b) -> a -> c

现在,当您编写(*) . (+)时,它实际上与(.) (*) (+)相同(即(*)(.)的第一个参数,(+)(.)的第二个参数):

代码语言:javascript
复制
(.) :: (b -> c) -> (a -> b) -> a -> c
       |______|    |______|
           |           |
          (*)         (+)

因此,(*)的类型签名(即Num x => x -> x -> x)与b -> c统一

代码语言:javascript
复制
(*) :: Num x => x -> x -> x -- remember that `x ->  x -> x`
                |    |____| -- is implicitly `x -> (x -> x)`
                |       |
                b ->    c

(.) (*) ::          (a -> b) -> a ->    c
                          |             |
                          |          |‾‾‾‾|
           Num x =>       x          x -> x

(.) (*) :: Num x => (a -> x) -> a -> x -> x

因此,(+)的类型签名(即Num y => y -> y -> y)与Num x => a -> x统一

代码语言:javascript
复制
(+) :: Num y => y -> y -> y -- remember that `y ->  y -> y`
                |    |____| -- is implicitly `y -> (y -> y)`
                |       |
       Num x => a ->    x

(.) (*) (+) ::  Num x                => a ->     x    ->    x
                                        |        |          |
                                        |     |‾‾‾‾|     |‾‾‾‾|
                              Num y  => y     y -> y     y -> y

(.) (*) (+) :: (Num (y -> y), Num y) => y -> (y -> y) -> y -> y

我希望这能澄清Num (y -> y)Num y是从哪里来的。留给您的是一个非常奇怪的(Num (y -> y), Num y) => y -> (y -> y) -> y -> y类型的函数。

令人感到奇怪的是,它期望yy -> y都是Num的实例。y应该是Num的一个实例,这是可以理解的,但是y -> y是如何实现的呢?让y -> y成为Num的一个实例似乎不合逻辑。那不可能是对的。

但是,当您查看功能组合的实际功能时,这是有意义的:

代码语言:javascript
复制
( f  .  g ) = \z ->  f  ( g  z)

((*) . (+)) = \z -> (*) ((+) z)

所以你有一个函数\z -> (*) ((+) z)。因此,z显然必须是Num的一个实例,因为(+)被应用于它。因此,\z -> (*) ((+) z)的类型是Num t => t -> ...,其中...(*) ((+) z)的类型,我们稍后会发现这一点。

因此,((+) z)Num t => t -> t类型的,因为它需要多一个数字。然而,在将其应用于另一个数字之前,(*)将被应用于它。

因此,(*)希望((+) z)Num的一个实例,这就是为什么希望t -> tNum的一个实例。因此,将...替换为(t -> t) -> t -> t,并添加约束Num (t -> t),从而生成(Num (t -> t), Num t) => t -> (t -> t) -> t -> t类型。

您真正想要将(*)(+)结合起来的方法是使用(.:)

代码语言:javascript
复制
(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
f .: g = \x y -> f (g x y)

因此,(*) .: (+)\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(.:)也可以定义为(.:) = (.) . (.)。你可以在这里读到更多关于它的内容:

什么是(f .)是在Haskell吗?

希望这能有所帮助。

票数 67
EN
查看全部 5 条回答
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/27664213

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档