首页 文章

什么可以证明lambda表达式中的参数传递给monad?

提问于
浏览
0

相对于鱼类操作者,Monads满足相关性 .

(h >=> g) >=> f = h >=> ( g >=> f)

这转换为bind(与lambda表达式)看起来像,

\a -> h a >>=(\b -> g b >>= \c -> f c) = 
\a ->(h a >>= \b -> g b)>>= \c -> f c

这意味着下面的等式是明确的

( a -> h a >>= \b -> g b >>= \c -> f c ) =  h >=> g >=> f

这是理解Monadic构图的好方法 .

但是并非所有Monadic代码都将绑定变量保持为lambdas分开 . 例如,

[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch) = 
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]

返回中的“n”是从顶部lambda获得的 .

更普遍,

a -> g a >>= \b -> f a b

f取决于上面的a和b . 用f,g和(> =>)定义上述给出

\a -> (\x -> g a) >=> f a

我不太懂 . 它与上面我很清楚的方程式不符 . 我认为鱼是这里的基本概念,我试图理解它是如何与我写的典型Monads相互作用的 . 我想更好地理解上述内容 .

解决此问题的一种方法是尝试从List表达式语法中获取含义

[ (n,ch) | n <- [1, 2], ch <- ['a', 'b'] ]

我认为这意味着一种对称性 .

连接lambda表达式和Monads有没有很好的对称性?还是我读了太多这个?我对高度嵌套的lambda表达式的恐惧是错误还是合理?

3 回答

  • 2

    不,没有't any restrictions. Once you' ve绑定一个lambda你可以做anything . 这是 Applicative 优先于 Monad 的原因之一,因为它更弱(因此给你更强的自由定理限制) .

    ( [1,2] >>= \n -> "ab" >>= \ch -> return (n,ch) )
       ≡  (,) <$> [1,2] <*> "ab"
       ≡  liftA2 (,) [1,2] "ab"
       ≈  liftA2 (flip (,)) "ab" [1,2]
    

    最后一个实际上不是一个合适的等式;适用法律只保证这些表达式的值相同见评论,但结构可以而且会有所不同 .

    Prelude Control.Applicative> liftA2 (,) [1,2] "ab"
    [(1,'a'),(1,'b'),(2,'a'),(2,'b')]
    Prelude Control.Applicative> liftA2 (flip (,)) "ab" [1,2]
    [(1,'a'),(2,'a'),(1,'b'),(2,'b')]
    
  • 2

    您的问题的另一个想法:Monad是最常见的,因为效果可能取决于输入 . 输入 a 并产生输出 b 的monadic计算 m 可写为 a -> m b . 由于这是一个函数,我们(可以)使用lambdas定义这样的计算,这自然可以跨越右边 . 但这种普遍性使分析计算变得复杂,因为你的 \a -> g a >>= \b -> f a b .

    对于arrows(占据应用函子和monad之间的空间),情况有所不同 . 对于常规箭头,输入必须是显式的 - 箭头计算 arr 的常规类型为 arr a b . 因此,在箭头计算中跨越"forward"的输入必须使用Arrow基元显式地在那里进行线程化 .

    扩展你的例子

    {-# LANGUAGE Arrows #-}
    
    import Control.Arrow
    
    bind2 :: (Monad m) => (a -> m b) -> (a -> b -> m c) -> a -> m c
    bind2 g f = \a -> g a >>= \b -> f a b
    

    到箭头:函数 f 现在必须将一对作为其输入(因为箭头被定义为接受一个输入值) . 使用箭头 do 表示法,我们可以将其表达为

    bind2A :: (Arrow arr) => arr a b -> arr (a, b) c -> arr a c
    bind2A g f = proc a -> do
                    b <- g -< a
                    c <- f -< (a, b)
                    returnA -< c
    

    甚至更简单地使用 Arrow 原语:

    bind2A' :: (Arrow arr) => arr a b -> arr (a, b) c -> arr a c
    bind2A' g f = (returnA &&& g) >>> f
    

    图形:

    --------------->[   ]
       \            [ f ]---->
        \-->[ g ]-->[   ]
    

    由于不太通用,箭头允许在电路实际执行之前推断出有关电路的更多信息 . 一个很好的阅读是Understanding arrows,它描述了它们背后的原始动机 - 构建可以通过静态和动态部分避免空间泄漏的解析器 .

  • 5

    解决您的编辑问题,您可以在其中考虑如何编写...

    \ a - > g a >> = \ b - > f a b

    ...使用 (>=>) ,在这种情况下实际上没有丢失 . 退后一步并仔细考虑如何将 (>=>) 转换为 (>>=) ,反之亦然:

    f >=> g = \x -> f x >>= g
    m >>= f = (const m >=> f) () -- const x = \_ -> x
    

    在第二个等式中,即与您的关注点相关的等式,我们将第一个参数转换为 (>>=) 为一个函数,该函数可以通过 const 传递给 (>=>) . 由于 const m >=> f 是一个忽略其参数的函数,我们只需将 () 作为伪参数传递,以便恢复 (>>=) .

    既然如此,您可以使用第二个等式重写您的示例:

    \a -> g a >>= \b -> f a b
    \a -> (const (g a) >=> \b -> f a b) ()
    \a -> (const (g a) >=> f a) ()
    

    除了提供虚拟 () 的附加技巧之外,您在问题中获得了这一点 .

相关问题