两个函数的组合类型是什么——例如 (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
但是自己怎么办呢?
嗯,这里有三个函数在起作用:
注意,如果你使用(.)
函数作为运算符,你实际上写了:
(.) 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
表示a
和b
是同一类型)
因此(.) flip
的类型是
(.) flip :: (a -> b) -> (a -> c)
这又是一个只有一个参数的函数(Haskell 中的所有函数都有一个参数),并且该参数的类型为 a -> b
。
然后我们将该函数应用于 const
,因此我们再次进行模式匹配:
a -> b
g -> (h -> g)
所以这意味着 a ~ g
和 b ~ (d -> (e -> f)) ~ (h -> g)
,因此我们知道 d ~ h
和 g ~ (e -> f)
。
我们知道((.) flip) const
的类型有类型:
((.) flip) const :: a -> c`
所以现在是替换的问题:a
替换 g
和 g ~ (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))
;
- 垂直对齐这些东西有帮助;
- 在不同的类型中对类型变量进行编号有助于将它们分开。
我已经开始学习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
但是自己怎么办呢?
嗯,这里有三个函数在起作用:
注意,如果你使用(.)
函数作为运算符,你实际上写了:
(.) 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
表示a
和b
是同一类型)
因此(.) flip
的类型是
(.) flip :: (a -> b) -> (a -> c)
这又是一个只有一个参数的函数(Haskell 中的所有函数都有一个参数),并且该参数的类型为 a -> b
。
然后我们将该函数应用于 const
,因此我们再次进行模式匹配:
a -> b
g -> (h -> g)
所以这意味着 a ~ g
和 b ~ (d -> (e -> f)) ~ (h -> g)
,因此我们知道 d ~ h
和 g ~ (e -> f)
。
我们知道((.) flip) const
的类型有类型:
((.) flip) const :: a -> c`
所以现在是替换的问题:a
替换 g
和 g ~ (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))
; - 垂直对齐这些东西有帮助;
- 在不同的类型中对类型变量进行编号有助于将它们分开。