haskell - 当我在 Haskell 中用 + 组合 * 时会发生什么?

标签 haskell functional-programming function-composition

我试图理解

的结果
(*) . (+) 

在 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

但是编译失败。我试图在撰写这些内容时逐步完成逻辑步骤,但我没有完全理解它是如何得到这个结果的(以及结果是什么)。

最佳答案

我理解你的感受。我发现函数组合一开始也很难掌握。帮助我理解这个问题的是类型签名。考虑:

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

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

(.) :: (b -> c) -> (a -> b) -> a -> c
       |______|    |______|
           |           |
          (*)         (+)

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

(*) :: 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 统一:

(+) :: 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 的一个实例似乎不合逻辑。这不可能是正确的。

但是,当您查看函数组合实际执行的操作时,这是有意义的:

( 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 -> t预计是 Num 的实例。因此...替换为 (t -> t) -> t -> t和约束 Num (t -> t)添加后,得到类型 (Num (t -> t), Num t) => t -> (t -> t) -> t -> t .

您真正想要的组合方式 (*)(+)正在使用(.:) :

(.:) :: (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 ,和(.:)也可以定义为(.:) = (.) . (.) 。您可以在这里阅读更多相关信息:

What does (f .) . g mean in Haskell?

关于haskell - 当我在 Haskell 中用 + 组合 * 时会发生什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27664213/

相关文章:

haskell - stack new 命令无法下载 lts-14.1 的构建计划

list - 列表串联 “the correct”的方法(使用尾递归)

Haskell 函数组合与 Map 函数

python - 如何制作函数 Composer

haskell - 静态强制两个对象是从同一个 (Int) "seed"创建的

haskell - 我什么时候可以依赖 Haskell 懒惰地阅读列表?

haskell - Haskell 中的 `map` 是懒惰的吗?

python - 两个函数的乘积

带有重载提取器的 Scala 语言?

Scala:函数组合中的类型不匹配,发现 (Int, Int) => Seq[Int] require ? => 序列[整数]