繁体   English   中英

为什么加入。 (翻转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的类型\\x -> join . x \\x -> join . x(Monad m) => (a -> m (mb)) -> (a -> mb)\\y -> y . (flip fmap)的类型\\y -> y . (flip fmap) \\y -> y . (flip fmap)(Functor f) => ((a -> b) -> fb) -> (fa -> c)

ghci版本8.2.2允许定义h = join . (flip fmap) h = join . (flip fmap)

为什么h有类型((A -> B) -> A) -> (A -> B) -> B

特别是,为什么仿函数和monad约束消失了? 这真的是正确和预期的行为吗? 作为后续行动,我还想问:

为什么评估整数uv h (\\f -> fu) (\\x -> x + v)在每种情况下给出u + 2v

简而言之 :由于类型推导,Haskell知道mf实际上是部分实例化的箭头。

派生类型

好吧,让我们做数学。 函数join . (flip fmap) join . (flip fmap)基本上是你给定的lambda表达式\\x -> join . x \\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)

(我们这里使用cd而不是ab来避免两种可能不同类型之间的混淆)。

这意味着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)

所以我们现在知道afc具有相同的类型(这是波浪号的含义~ )。

但我们必须做一些额外的计算:

  Monad m =>   m (m b)
~ Functor f => ((c -> d) -> f d)
--------------------------------
m ~ (->) (c -> d), m b ~ f d

因此我们知道m(->) (c -> d) (基本上这是一个我们知道输入类型的函数,这里(c -> d) ,输出类型是m的类型参数。

所以这意味着mb ~ (c -> d) -> b ~ fd ,所以这意味着f ~ (->) (c -> d)b ~ d 另外一个结果是,因为a ~ fc ,我们知道a ~ (c -> d) -> c

所以列出我们得到的东西:

f ~ m
m ~ (->) (c -> d)
b ~ d
a ~ (c -> d) -> c

所以我们现在可以“专门化”我们的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)的类型(\\x -> join . x) (flip fmap)是lambda表达式类型的结果类型,即:

(\x -> join . x) (flip fmap)
    :: ((c -> d) -> c) -> (c -> d) -> d

但是现在我们当然还没有获得这个功能的实现。 然而,我们已经向前迈进了一步。

推导实施

既然我们现在知道m ~ (->) (c -> d) ,我们知道应该查找monad箭头实例

 instance Monad ((->) r) where f >>= k = \\ r -> k (fr) r 

因此对于给定函数f :: r -> a ,作为左操作数,以及函数k :: a -> (r -> b) ~ a -> r -> b作为操作数,我们构造一个新的映射函数应用于f的变量xk应用于xx 因此,这是一种对输入变量x执行某种预处理的方法,然后在考虑预处理和原始视图的情况下进行处理(这是人类读者可以使用解释)。

现在join :: Monad m => m (ma) -> ma 实现为

 join :: Monad m => m (ma) -> ma join x = x >>= id 

所以对于(->) r monad,这意味着我们将其实现为:

-- specialized for `m ~ (->) a
join f = \r -> id (f r) r

由于id :: a -> a (标识函数)返回其参数,我们可以进一步简化它:

-- specialized for `m ~ (->) a
join f = \r -> (f r) r

或清洁:

-- specialized for `m ~ (->) a
join f x = f x x

所以它基本上给出了一个函数f ,然后将该参数两次应用于该函数。

此外,我们知道箭头类型的Functor实例定义为

 instance Functor ((->) r) where fmap = (.) 

因此它基本上用作函数结果的“后处理器”:我们构造一个新函数,用于使用给定函数进行后处理。

所以现在我们将这个函数专门用于给定的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基本上需要两个参数: ax ,然后将其与执行功能的应用程序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 (或者我们呢?)。 函数的joinfmap定义在边距中给出。 我们到了

(join . flip fmap) f g = g (f g)   -- f :: (a -> b) -> a,  g :: a -> b 
                                   -- f g :: a  , g (f g) :: b

另一种方式是从类型开始,按照modus ponens的规则,

            ((a -> b) -> a)    (a -> b)        --       f  g
            ---------------------------
(a -> b)                 a                     --  g   (f  g)
---------------------------------------
      b

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM