两个函数的组合类型是什么——例如 (flip.const)

What is the type of composition of two functions -- for example (flip . const)

我已经开始学习Haskell,我很好奇如何找出函数的组合类型:例如:

:t flip
flip :: (a -> b -> c) -> b -> a -> c

:t const
const :: a -> b -> a

如何手动完成:t (flip . const)

当然 GHCi 可以帮助您:

:t (flip.const)
(flip . const) :: (b -> c) -> b -> a -> c

但是自己怎么办呢?

嗯,这里有三个函数在起作用:

  1. (.) :: (b -> c) -> (a -> b) -> a -> c;
  2. flip :: (a -> b -> c) -> b -> a -> c;和
  3. const :: a -> b -> a.

注意,如果你使用(.)函数作为运算符,你实际上写了:

(.) flip const

或更详细:

((.) flip) const

现在让我们首先以冗长的方式编写函数的签名,并使用不同的可变名称,这样它们就不会冲突:

(.) :: (b -> c) -> ((a -> b) -> (a -> c))
flip :: (d -> (e -> f)) -> (e -> (d -> f))
const :: g -> (h -> g)

所以我们将 (.) 应用到 flip,这意味着我们必须将具有类型 (b -> c)(.) 的参数与 flip 的签名相匹配,所以我们解决这个问题:

b               -> c
(d -> (e -> f)) -> (e -> (d -> f))

这是唯一可能的匹配(注意括号)。所以这意味着:

b ~ (d -> (e -> f))
c ~ (e -> (d -> f))

(这里的a ~ b表示ab是同一类型)

因此(.) flip的类型是

(.) flip :: (a -> b) -> (a -> c)

这又是一个只有一个参数的函数(Haskell 中的所有函数都有一个参数),并且该参数的类型为 a -> b。 然后我们将该函数应用于 const,因此我们再次进行模式匹配:

a -> b
g -> (h -> g)

所以这意味着 a ~ gb ~ (d -> (e -> f)) ~ (h -> g),因此我们知道 d ~ hg ~ (e -> f)

我们知道((.) flip) const的类型有类型:

((.) flip) const :: a -> c`

所以现在是替换的问题:a 替换 gg ~ (e -> f),所以 a ~ (e -> f)。此外我们知道 c ~ (e -> (d -> f)),所以这意味着类型是:

((.) flip) const :: (e -> f) -> (e -> (d -> f))

或更简洁的形式:

flip . const :: (e -> f) -> e -> d -> f

除变量重命名外,与 GHCi 派生的类型相同。

我们还有 (>>>) = flip (.) 可以更容易处理,类型方面:

f . g = g >>> f

g ::       a -> b
f ::            b -> c
g >>> f :: a ->      c

因此

flip . const = const >>> flip

const :: a1 -> (b1 ->     a1    )
flip ::        (a2 -> (b2 -> c2)) -> (b2 -> a2 -> c2)
const >>> flip 
     ::  a1 ->                        b2 -> a2 -> c2      -- where
--           b1 ~ a2,   a1 ~ b2 -> c2
     ::  (b2 -> c2) ->                b2 -> a2 -> c2

flip . const :: (b -> c) -> b -> a -> c。 GHCi 也这么说。

从这个类型我们立即看到 (flip . const) f x z = f x。确实(flip . const) f x z = flip (const f) x z = const f z x = f x.

从中吸取的三个教训:

  • 类型关联到右边,因为功能应用程序关联到左边,f x y z = (((f x) y) z)f :: a -> (b -> (c -> d))
  • 垂直对齐这些东西有帮助;
  • 在不同的类型中对类型变量进行编号有助于将它们分开。