Haskell 关于lambda表达式中传递到monad的参数,可以证明什么?

Haskell 关于lambda表达式中传递到monad的参数,可以证明什么?,haskell,monads,Haskell,Monads,相对于fish算子,monad满足结合性 (h >=> g) >=> f = h >=> ( g >=> f) 将其转换为与lambda表达式绑定,如下所示: \a -> h a >>=(\b -> g b >>= \c -> f c) = \a ->(h a >>= \b -> g b)>>= \c -> f c 这意味着下面的等式是明确的 ( a ->

相对于fish算子,monad满足结合性

(h >=> g) >=> f = h >=> ( g >=> f)
将其转换为与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
这是理解一元合成的好方法

然而,并非所有的一元代码都将绑定到lambda的变量分开。比如说,

[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch) = 
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
返回值中的n来自顶部λ

更一般地说

a -> g a >>= \b -> f a b
f取决于上面的a和b。根据f、g和>=>定义上述内容

\a -> (\x -> g a) >=> f a 
我不太明白。它与我展示的上面的等式不匹配。我把鱼看作是这里的基本概念,我试图理解它是如何与我写的典型单子相互作用的。我想更好地理解以上内容

实现这一点的一种方法是尝试从列表表达式语法中获取含义

[ (n,ch) | n <- [1, 2], ch <- ['a', 'b'] ]
我认为这意味着一种对称


lambda表达式和monad之间有什么好的对称性吗?还是我读得太多了?我对高度嵌套的lambda表达式的恐惧是错误的还是合理的?

不,没有任何限制。一旦你绑定了一个lambda,你就可以这么做了。这是Applicative比Monad更受欢迎的原因之一,因为它较弱,因此会给你更强的自由定理限制

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

< >编辑您的编辑,在其中考虑如何编写……/P> 。。。使用>=>,在这种情况下实际上不会丢失任何东西。向后退一步,准确地考虑如何> > >可以转换成>=,反之亦然:

是有帮助的。
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) ()

除了提供一个假人的额外技巧外,这就是你在问题中得到的

你问题的另一个想法是:单子最普遍的意义是效果可以依赖于输入。接受输入A并产生输出b的一元计算m可以写成A->MB。由于这是一个函数,我们可以使用lambdas定义这样的计算,它可以自然地扩展到右边。但是这种通用性使分析计算复杂化,因为您的\a->ga>=\b->fab

对于占据应用函子和单子之间空间的函数,情况有所不同。对于一般箭头,输入必须是显式的-箭头计算arr的一般类型为arrab。因此,在箭头计算中向前延伸的输入必须使用箭头原语显式线程化

来扩展您的示例

{-# 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
to arrows:函数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
以图形方式:

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

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

我根本不理解这个问题。直觉上,我担心嵌套lambda之间的微妙耦合。-你能多说一点你担心什么吗?@duplode我的编辑澄清了我的担忧吗?它确实;老实说,我不太确定应用定律所暗示的fxy和flip fyx之间的关系;我很确定他们说了一些关于对称性的东西,但我一直很难正确理解这些定律。关于你的评论:状态是fxy和flip fyx之间值相同的反例-例如,runState/State 2*&&&3*State+4&&subtract 3 1是1/7,0,但是运行状态翻转/状态+4&&&subtract 3状态2*&&3*1是-4/5,-6。更一般地说,应用程序接口本身并不支持效果之间的依赖性,但它的存在也不会阻止这种依赖性。我认为StateT可能比State做得更好,因为它允许几乎任意的依赖。
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
bind2A' :: (Arrow arr) => arr a b -> arr (a, b) c -> arr a c
bind2A' g f = (returnA &&& g) >>> f
--------------->[   ]
   \            [ f ]---->
    \-->[ g ]-->[   ]