为什么加入 . (flip fmap) 有类型 ((A -> B) -> A) -> (A -> B) -> B?
Why does join . (flip fmap) have type ((A -> B) -> A) -> (A -> B) -> B?
一些人在 ghci 中使用仿函数和 monad 让我找到了一个值,我想更好地理解它的类型和行为。
\x -> join . x
的类型是(Monad m) => (a -> m (m b)) -> (a -> m b)
,\y -> y . (flip fmap)
的类型是(Functor f) => ((a -> b) -> f b) -> (f a -> c)
。
ghci 版本 8.2.2 允许定义 h = join . (flip fmap)
.
Why does h
have type ((A -> B) -> A) -> (A -> B) -> B
?
特别是,为什么 functor 和 monad 约束消失了?这真的是正确的和预期的行为吗?作为后续,我还想问一下:
Why does evaluating h (\f -> f u) (\x -> x + v)
for integers u
and v
give u + 2v
in every case?
简而言之:由于类型推导,Haskell知道m
和f
实际上是部分实例化的箭头。
派生类型
好吧,让我们来计算一下。函数 join . (flip fmap)
基本上是你给定的 lambda 表达式 \x -> join . x
和参数 (flip fmap)
,所以:
h = (\x -> join . x) (flip fmap)
现在 lambda 表达式的类型为:
(\x -> join . x) :: Monad m => (a -> m (m b)) -> (a -> m b)
现在参数 flip fmap
的类型为:
flip fmap :: Functor f => f c -> ((c -> d) -> f d)
(我们在这里使用c
和d
而不是a
和b
以避免混淆两个可能不同类型)。
所以这意味着 flip fmap
的类型与 lambda 表达式的 参数 的类型相同,因此我们知道:
Monad m => a -> m (m b)
~ Functor f => f c -> ((c -> d) -> f d)
---------------------------------------
a ~ f c, m (m b) ~ ((c -> d) -> f d)
所以我们现在知道 a
与 f c
具有相同的类型(这是代字号 ~
的含义)。
但是我们必须做一些额外的计算:
Monad m => m (m b)
~ Functor f => ((c -> d) -> f d)
--------------------------------
m ~ (->) (c -> d), m b ~ f d
因此我们知道 m
与 (->) (c -> d)
相同(基本上这是一个我们知道输入类型的函数,这里是 (c -> d)
,输出类型是一个类型m
.
的参数
所以这意味着m b ~ (c -> d) -> b ~ f d
,所以这意味着f ~ (->) (c -> d)
和b ~ d
。一个额外的结果是,由于 a ~ f c
,我们知道 a ~ (c -> d) -> c
所以列出我们得出的结果:
f ~ m
m ~ (->) (c -> d)
b ~ d
a ~ (c -> d) -> c
所以我们现在可以 "specialize" 我们的 lambda 表达式和我们的 flip fmap
函数的类型:
(\x -> join . x)
:: (((c -> d) -> c) -> (c -> d) -> (c -> d) -> d) -> ((c -> d) -> c) -> (c -> d) -> d
flip fmap
:: ((c -> d) -> c) -> (c -> d) -> (c -> d) -> d
和 flip fmap
的类型现在与 lambda 表达式的参数类型完美匹配。所以(\x -> join . x) (flip fmap)
的类型就是lambda表达式类型的结果类型,也就是:
(\x -> join . x) (flip fmap)
:: ((c -> d) -> c) -> (c -> d) -> d
但是现在我们当然还没有得到这个函数的实现。然而,我们已经更进一步了。
派生实现
因为我们现在知道 m ~ (->) (c -> d)
,我们知道我们应该查找 arrow instance of a monad:
instance Monad ((->) r) where
f >>= k = \ r -> k (f r) r
所以对于一个给定的函数f :: r -> a
,作为左操作数,一个函数k :: a -> (r -> b) ~ a -> r -> b
作为操作数,我们构造一个新函数,将变量x
映射到k
应用于 f
应用于 x
,以及 x
。因此,这是一种对输入变量 x
执行某种预处理的方法,然后在考虑预处理和原始视图的情况下进行处理(这是 an 人类 reader 可以使用的解释)。
现在join :: Monad m => m (m a) -> m a
is implemented as:
join :: Monad m => m (m a) -> m a
join x = x >>= id
所以对于 (->) r
monad,这意味着我们将其实现为:
-- specialized for `m ~ (->) a
join f = \r -> id (f r) r
由于id :: a -> a
(恒等函数)returns其参数,我们可以进一步简化为:
-- specialized for `m ~ (->) a
join f = \r -> (f r) r
或清洁工:
-- specialized for `m ~ (->) a
join f x = f x x
所以它基本上是给定一个函数f
,然后将一个参数应用到该函数两次。
此外我们知道箭头类型的 Functor
实例是 defined as:
instance Functor ((->) r) where
fmap = (.)
所以它基本上用作函数结果的"post processor":我们构造一个新函数,它将使用给定函数进行post处理。
现在我们已经为给定的 Functor
/Monad
专门化了函数,我们可以推导出实现为:
-- alternative implementation
h = (.) (\f x -> f x x) (flip (.))
或者使用更多的 lambda 表达式:
h = \a -> (\f x -> f x x) ((flip (.)) a)
我们现在可以进一步专门化为:
h = \a -> (\f x -> f x x) ((\y z -> z . y) a)
-- apply a in the lambda expression
h = \a -> (\f x -> f x x) (\z -> z . a)
-- apply (\z -> z . a) in the first lambda expression
h = \a -> (\x -> (\z -> z . a) x x)
-- cleaning syntax
h a = (\x -> (\z -> z . a) x x)
-- cleaning syntax
h a x = (\z -> z . a) x x
-- apply lambda expression
h a x = (x . a) x
-- remove the (.) part
h a x = x (a x)
所以h
基本上接受两个参数:a
和x
,然后以a
为函数,x
为参数执行函数应用,并且输出再次传递给 x
函数。
示例用法
您使用的示例用法:
h (\f -> f u) (\x -> x + v)
或更好:
h (\f -> f u) (+v)
所以我们可以这样分析:
h (\f -> f u) (+v)
-> (+v) ((\f -> f u) (+v))
-> (+v) ((+v) u)
-> (+v) (u+v)
-> ((u+v)+v)
所以我们将u+v
添加到v
。
类型排队更容易 >>>
:
a -> b >>>
b -> c ::
a -> c
在这里,我们有
join . flip fmap == flip fmap >>> join
flip fmap :: Functor f => f a -> ((a -> b) -> f b )
join :: Monad m => (m (m b)) -> m b
----------------------------------------------------------
flip fmap >>> join ::
(Functor f, Monad m) => f a -> m b , ((a -> b) ->) ~ m, f ~ m
::
(Functor f, Monad f) => f a -> f b , f ~ ((a -> b) ->)
:: ((a -> b) -> a) -> ((a -> b) -> b)
简单、机械、平凡。
要看看它做什么,组合样式定义通常最容易玩弄,
(join . flip fmap) f g x =
join (flip fmap f) g x = -- join f x = f x x
(`fmap` f) g g x = -- f `fmap` g = f . g
(g . f) g x
g (f g) x
所以我们毕竟不需要 x
(或者我们需要吗?)。函数的 join
和 fmap
定义在空白处给出。我们已经到达
(join . flip fmap) f g = g (f g) -- f :: (a -> b) -> a, g :: a -> b
-- f g :: a , g (f g) :: b
另一种方法是从类型开始,按照先决条件,
((a -> b) -> a) (a -> b) -- f g
---------------------------
(a -> b) a -- g (f g)
---------------------------------------
b
一些人在 ghci 中使用仿函数和 monad 让我找到了一个值,我想更好地理解它的类型和行为。
\x -> join . x
的类型是(Monad m) => (a -> m (m b)) -> (a -> m b)
,\y -> y . (flip fmap)
的类型是(Functor f) => ((a -> b) -> f b) -> (f a -> c)
。
ghci 版本 8.2.2 允许定义 h = join . (flip fmap)
.
Why does
h
have type((A -> B) -> A) -> (A -> B) -> B
?
特别是,为什么 functor 和 monad 约束消失了?这真的是正确的和预期的行为吗?作为后续,我还想问一下:
Why does evaluating
h (\f -> f u) (\x -> x + v)
for integersu
andv
giveu + 2v
in every case?
简而言之:由于类型推导,Haskell知道m
和f
实际上是部分实例化的箭头。
派生类型
好吧,让我们来计算一下。函数 join . (flip fmap)
基本上是你给定的 lambda 表达式 \x -> join . x
和参数 (flip fmap)
,所以:
h = (\x -> join . x) (flip fmap)
现在 lambda 表达式的类型为:
(\x -> join . x) :: Monad m => (a -> m (m b)) -> (a -> m b)
现在参数 flip fmap
的类型为:
flip fmap :: Functor f => f c -> ((c -> d) -> f d)
(我们在这里使用c
和d
而不是a
和b
以避免混淆两个可能不同类型)。
所以这意味着 flip fmap
的类型与 lambda 表达式的 参数 的类型相同,因此我们知道:
Monad m => a -> m (m b)
~ Functor f => f c -> ((c -> d) -> f d)
---------------------------------------
a ~ f c, m (m b) ~ ((c -> d) -> f d)
所以我们现在知道 a
与 f c
具有相同的类型(这是代字号 ~
的含义)。
但是我们必须做一些额外的计算:
Monad m => m (m b)
~ Functor f => ((c -> d) -> f d)
--------------------------------
m ~ (->) (c -> d), m b ~ f d
因此我们知道 m
与 (->) (c -> d)
相同(基本上这是一个我们知道输入类型的函数,这里是 (c -> d)
,输出类型是一个类型m
.
所以这意味着m b ~ (c -> d) -> b ~ f d
,所以这意味着f ~ (->) (c -> d)
和b ~ d
。一个额外的结果是,由于 a ~ f c
,我们知道 a ~ (c -> d) -> c
所以列出我们得出的结果:
f ~ m
m ~ (->) (c -> d)
b ~ d
a ~ (c -> d) -> c
所以我们现在可以 "specialize" 我们的 lambda 表达式和我们的 flip fmap
函数的类型:
(\x -> join . x)
:: (((c -> d) -> c) -> (c -> d) -> (c -> d) -> d) -> ((c -> d) -> c) -> (c -> d) -> d
flip fmap
:: ((c -> d) -> c) -> (c -> d) -> (c -> d) -> d
和 flip fmap
的类型现在与 lambda 表达式的参数类型完美匹配。所以(\x -> join . x) (flip fmap)
的类型就是lambda表达式类型的结果类型,也就是:
(\x -> join . x) (flip fmap)
:: ((c -> d) -> c) -> (c -> d) -> d
但是现在我们当然还没有得到这个函数的实现。然而,我们已经更进一步了。
派生实现
因为我们现在知道 m ~ (->) (c -> d)
,我们知道我们应该查找 arrow instance of a monad:
instance Monad ((->) r) where f >>= k = \ r -> k (f r) r
所以对于一个给定的函数f :: r -> a
,作为左操作数,一个函数k :: a -> (r -> b) ~ a -> r -> b
作为操作数,我们构造一个新函数,将变量x
映射到k
应用于 f
应用于 x
,以及 x
。因此,这是一种对输入变量 x
执行某种预处理的方法,然后在考虑预处理和原始视图的情况下进行处理(这是 an 人类 reader 可以使用的解释)。
现在join :: Monad m => m (m a) -> m a
is implemented as:
join :: Monad m => m (m a) -> m a join x = x >>= id
所以对于 (->) r
monad,这意味着我们将其实现为:
-- specialized for `m ~ (->) a
join f = \r -> id (f r) r
由于id :: a -> a
(恒等函数)returns其参数,我们可以进一步简化为:
-- specialized for `m ~ (->) a
join f = \r -> (f r) r
或清洁工:
-- specialized for `m ~ (->) a
join f x = f x x
所以它基本上是给定一个函数f
,然后将一个参数应用到该函数两次。
此外我们知道箭头类型的 Functor
实例是 defined as:
instance Functor ((->) r) where fmap = (.)
所以它基本上用作函数结果的"post processor":我们构造一个新函数,它将使用给定函数进行post处理。
现在我们已经为给定的 Functor
/Monad
专门化了函数,我们可以推导出实现为:
-- alternative implementation
h = (.) (\f x -> f x x) (flip (.))
或者使用更多的 lambda 表达式:
h = \a -> (\f x -> f x x) ((flip (.)) a)
我们现在可以进一步专门化为:
h = \a -> (\f x -> f x x) ((\y z -> z . y) a)
-- apply a in the lambda expression
h = \a -> (\f x -> f x x) (\z -> z . a)
-- apply (\z -> z . a) in the first lambda expression
h = \a -> (\x -> (\z -> z . a) x x)
-- cleaning syntax
h a = (\x -> (\z -> z . a) x x)
-- cleaning syntax
h a x = (\z -> z . a) x x
-- apply lambda expression
h a x = (x . a) x
-- remove the (.) part
h a x = x (a x)
所以h
基本上接受两个参数:a
和x
,然后以a
为函数,x
为参数执行函数应用,并且输出再次传递给 x
函数。
示例用法
您使用的示例用法:
h (\f -> f u) (\x -> x + v)
或更好:
h (\f -> f u) (+v)
所以我们可以这样分析:
h (\f -> f u) (+v)
-> (+v) ((\f -> f u) (+v))
-> (+v) ((+v) u)
-> (+v) (u+v)
-> ((u+v)+v)
所以我们将u+v
添加到v
。
类型排队更容易 >>>
:
a -> b >>>
b -> c ::
a -> c
在这里,我们有
join . flip fmap == flip fmap >>> join
flip fmap :: Functor f => f a -> ((a -> b) -> f b )
join :: Monad m => (m (m b)) -> m b
----------------------------------------------------------
flip fmap >>> join ::
(Functor f, Monad m) => f a -> m b , ((a -> b) ->) ~ m, f ~ m
::
(Functor f, Monad f) => f a -> f b , f ~ ((a -> b) ->)
:: ((a -> b) -> a) -> ((a -> b) -> b)
简单、机械、平凡。
要看看它做什么,组合样式定义通常最容易玩弄,
(join . flip fmap) f g x =
join (flip fmap f) g x = -- join f x = f x x
(`fmap` f) g g x = -- f `fmap` g = f . g
(g . f) g x
g (f g) x
所以我们毕竟不需要 x
(或者我们需要吗?)。函数的 join
和 fmap
定义在空白处给出。我们已经到达
(join . flip fmap) f g = g (f g) -- f :: (a -> b) -> a, g :: a -> b
-- f g :: a , g (f g) :: b
另一种方法是从类型开始,按照先决条件,
((a -> b) -> a) (a -> b) -- f g
---------------------------
(a -> b) a -- g (f g)
---------------------------------------
b