[英]Why does join . (flip fmap) have type ((A -> B) -> A) -> (A -> B) -> B?
Some playing around with functors and monads in ghci led me to a value whose type and behaviour I would like to understand better. 有些人在ghci玩弄着仿函数和monad,这让我得到了一个我希望更好理解其类型和行为的价值。
The type of \\x -> join . x
\\x -> join . x
的类型\\x -> join . x
\\x -> join . x
is (Monad m) => (a -> m (mb)) -> (a -> mb)
and the type of \\y -> y . (flip fmap)
\\x -> join . x
是(Monad m) => (a -> m (mb)) -> (a -> mb)
和\\y -> y . (flip fmap)
的类型\\y -> y . (flip fmap)
\\y -> y . (flip fmap)
is (Functor f) => ((a -> b) -> fb) -> (fa -> c)
. \\y -> y . (flip fmap)
是(Functor f) => ((a -> b) -> fb) -> (fa -> c)
。
Version 8.2.2 of ghci permits the definition h = join . (flip fmap)
ghci版本8.2.2允许定义
h = join . (flip fmap)
h = join . (flip fmap)
. h = join . (flip fmap)
。
Why does
h
have type((A -> B) -> A) -> (A -> B) -> B
?为什么
h
有类型((A -> B) -> A) -> (A -> B) -> B
?
In particular, why do the functor and monad constraints disappear? 特别是,为什么仿函数和monad约束消失了? Is this really the correct and expected behaviour?
这真的是正确和预期的行为吗? As a follow up, I would also like to ask:
作为后续行动,我还想问:
Why does evaluating
h (\\f -> fu) (\\x -> x + v)
for integersu
andv
giveu + 2v
in every case?为什么评估整数
u
和v
h (\\f -> fu) (\\x -> x + v)
在每种情况下给出u + 2v
?
In short : due to type deduction, Haskell knows that m
and f
are in fact a partially instantiated arrow. 简而言之 :由于类型推导,Haskell知道
m
和f
实际上是部分实例化的箭头。
Well let us do the math. 好吧,让我们做数学。 The function
join . (flip fmap)
函数
join . (flip fmap)
join . (flip fmap)
is basically your given lambda expression \\x -> join . x
join . (flip fmap)
基本上是你给定的lambda表达式\\x -> join . x
\\x -> join . x
with as argument (flip fmap)
, so: \\x -> join . x
带有参数(flip fmap)
,所以:
h = (\x -> join . x) (flip fmap)
Now the lambda expression has type: 现在lambda表达式有类型:
(\x -> join . x) :: Monad m => (a -> m (m b)) -> (a -> m b)
Now the argument flip fmap
has type: 现在参数
flip fmap
有类型:
flip fmap :: Functor f => f c -> ((c -> d) -> f d)
(we here use c
and d
instead of a
and b
to avoid confusion between two possibly different types). (我们这里使用
c
和d
而不是a
和b
来避免两种可能不同类型之间的混淆)。
So that means that the type of flip fmap
is the same as the type of the argument of the lambda expression, hence we know that: 这意味着
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)
So we now know that a
has the same type as fc
(this is the meaning of the tilde ~
). 所以我们现在知道
a
与fc
具有相同的类型(这是波浪号的含义~
)。
But we have to do some extra computations: 但我们必须做一些额外的计算:
Monad m => m (m b)
~ Functor f => ((c -> d) -> f d)
--------------------------------
m ~ (->) (c -> d), m b ~ f d
Hence we know that m
is the same as (->) (c -> d)
(basically this is a function where we know that input type, here (c -> d)
, and the output type is a type parameter of m
. 因此我们知道
m
与(->) (c -> d)
(基本上这是一个我们知道输入类型的函数,这里(c -> d)
,输出类型是m
的类型参数。
So that means that mb ~ (c -> d) -> b ~ fd
, so this means that f ~ (->) (c -> d)
and b ~ d
. 所以这意味着
mb ~ (c -> d) -> b ~ fd
,所以这意味着f ~ (->) (c -> d)
和b ~ d
。 An extra consequence is that since a ~ fc
, we know that a ~ (c -> d) -> c
另外一个结果是,因为
a ~ fc
,我们知道a ~ (c -> d) -> c
So to list what we derived: 所以列出我们得到的东西:
f ~ m
m ~ (->) (c -> d)
b ~ d
a ~ (c -> d) -> c
So we now can "specialize" the types of both our lambda expression, and our flip fmap
function: 所以我们现在可以“专门化”我们的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
and type of flip fmap
now perfectly matches with the type of the argument of the lambda expression. 和
flip fmap
的类型现在完全匹配lambda表达式的参数类型。 So the type of (\\x -> join . x) (flip fmap)
is the result type of the lambda expression type, and that is: 所以
(\\x -> join . x) (flip fmap)
的类型(\\x -> join . x) (flip fmap)
是lambda表达式类型的结果类型,即:
(\x -> join . x) (flip fmap)
:: ((c -> d) -> c) -> (c -> d) -> d
But now we of course did not yet obtained the implementation of this function. 但是现在我们当然还没有获得这个功能的实现。 We are however already a step further.
然而,我们已经向前迈进了一步。
Since we now know that m ~ (->) (c -> d)
, we know we should lookup the arrow instance of a monad : 既然我们现在知道
m ~ (->) (c -> d)
,我们知道应该查找monad的箭头实例 :
instance Monad ((->) r) where f >>= k = \\ r -> k (fr) r
So for a given function f :: r -> a
, as left operand, and a function k :: a -> (r -> b) ~ a -> r -> b
as operand, we construct a new function that maps a variable x
to k
applied to f
applied to x
, and x
. 因此对于给定函数
f :: r -> a
,作为左操作数,以及函数k :: a -> (r -> b) ~ a -> r -> b
作为操作数,我们构造一个新的映射函数应用于f
的变量x
到k
应用于x
和x
。 It is thus a way to perform some sort of preprocessing on an input variable x
, and then do the processing both taking into account the preprocessing and the original view (well this is an interpretation a human reader can use). 因此,这是一种对输入变量
x
执行某种预处理的方法,然后在考虑预处理和原始视图的情况下进行处理(这是人类读者可以使用的解释)。
Now join :: Monad m => m (ma) -> ma
is implemented as : 现在
join :: Monad m => m (ma) -> ma
实现为 :
join :: Monad m => m (ma) -> ma join x = x >>= id
So for the (->) r
monad, this means that we implement this as: 所以对于
(->) r
monad,这意味着我们将其实现为:
-- specialized for `m ~ (->) a
join f = \r -> id (f r) r
Since id :: a -> a
(the identity function) returns its argument, we can further simplify it to: 由于
id :: a -> a
(标识函数)返回其参数,我们可以进一步简化它:
-- specialized for `m ~ (->) a
join f = \r -> (f r) r
or cleaner: 或清洁:
-- specialized for `m ~ (->) a
join f x = f x x
So it basically is given a function f
, and will then apply an argument twice to that function. 所以它基本上给出了一个函数
f
,然后将该参数两次应用于该函数。
Furthermore we know that the Functor
instance for the arrow type is defined as : 此外,我们知道箭头类型的
Functor
实例定义为 :
instance Functor ((->) r) where fmap = (.)
So it is basically used as a "post processor" on the result of the function: we construct a new function that will do the post processing with the given function. 因此它基本上用作函数结果的“后处理器”:我们构造一个新函数,用于使用给定函数进行后处理。
So now that we specialized the function enough for the given Functor
/ Monad
, we can derive the implementation as: 所以现在我们将这个函数专门用于给定的
Functor
/ Monad
,我们可以将实现派生为:
-- alternative implementation
h = (.) (\f x -> f x x) (flip (.))
or by using more lambda expressions: 或者使用更多的lambda表达式:
h = \a -> (\f x -> f x x) ((flip (.)) a)
which we can now further specialize as: 我们现在可以进一步专注于:
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)
So h
basically takes two arguments: a
and x
, it then performs function application with a
as function and x
as parameter, and the output is passed to the x
function again. 所以
h
基本上需要两个参数: a
和x
,然后将其与执行功能的应用程序a
作为函数和x
为参数,输出被传递给x
功能一次。
As sample usage you use: 作为样本用法,您使用:
h (\f -> f u) (\x -> x + v)
or nicer: 还是更好的:
h (\f -> f u) (+v)
so we can analyze this like: 所以我们可以这样分析:
h (\f -> f u) (+v)
-> (+v) ((\f -> f u) (+v))
-> (+v) ((+v) u)
-> (+v) (u+v)
-> ((u+v)+v)
So we add u+v
to v
. 所以我们将
u+v
添加到v
。
Types line up easier with >>>
: 使用
>>>
可以更轻松地排列类型:
a -> b >>>
b -> c ::
a -> c
Here, we have 在这里,我们有
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)
Simple, mechanical, mundane. 简单,机械,世俗。
To see what it does , combinatory style definitions are usually easiest to twiddle with, 要看看会发生什么 ,组合子样式定义通常是最容易给摆弄,
(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
So we don't need x
after all (or do we?). 所以我们毕竟不需要
x
(或者我们呢?)。 The join
and fmap
definitions for functions are given in the margins. 函数的
join
和fmap
定义在边距中给出。 We've arrived at 我们到了
(join . flip fmap) f g = g (f g) -- f :: (a -> b) -> a, g :: a -> b
-- f g :: a , g (f g) :: b
Another way is starting from the types, going by the rule of modus ponens, 另一种方式是从类型开始,按照modus ponens的规则,
((a -> b) -> a) (a -> b) -- f g
---------------------------
(a -> b) a -- g (f g)
---------------------------------------
b
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.