在过去的几个月里,我一直在这里和那里读一些业余时间阅读Monads。从大学时代开始,我就没有使用过函数式语言。所以我真的不记得Haskell,当然也不知道Scalaz。
尝试将这些食物与大量的Haskell代码相关联时,尝试了解洋葱,墨西哥卷饼和三明治。幸运的是,我偶然发现了两个关键的文章,这些文章给了我一个时刻:图片中的monad,以及来自命令式编码背景的另一个人,他只是写了相当于:
a -> b becomes m[a] -> m[b] (functor)
a -> m[b] becomes m[a] -> m[b] (monad)
and applicative is just a "function with context"
与描述bind目的的最简单的句子一起,许多类别的endofunctors爆炸式增长,并且变得简单。
身份,列表,可能和其他人突然变得有意义。在这些知识的推动下,我开始着眼于使用monadic类型在C ++中尝试一些TMP,看看它将如何发展。
我很快想传播国家。我想我现在阅读的关于州monad和monad变形金刚的文章比我为monad开始做的更多,但对于我的生活,我无法想象它们。许多键盘已经磨损了我确信这些主题上的所有单词都会回答像我这样的人。
但我要求你再忍受一次。
a -> s -> (b, s)
函数接受一个值和一些状态,返回一个新值和(可能)修改状态。 Bind显然需要将返回值和状态传播到下一个函数中。
我的问题是:monad的工作是因为他们强加了一个结构。坚持结构并遵守法律会带来欢乐时光和彩虹。但是a - > s - > (b,s)看起来不像monadic a - >米并[b]。
即使应用'a',它仍然是s - > (b,s)。区别在于后一个函数采用(monadic?)STATE并返回带有值的状态。而常规形式是:取一个VALUE并返回一个WRAPPED值。
进入的参数和返回类型的形式都有所不同。然而,许多文章都说这显然看起来像一个单子。这肯定不适合我。
如果我要放松观点,我必须被告知:而不是将m [a]绑定到a - > m [b],它接受任何对monad有意义的参数并将其应用于任何有意义的函数签名......然后我可以看到它工作。
在那种情况下,我可以简单地说“哦,这个monad将State [s]和'a'绑定到像 - > State [s] - >(State [s],b)”这样的函数。然后它可以期望一个元组返回并将其解压缩到下一个函数的参数中。
但不知怎的,我怀疑有一种方法可以让状态monad像所有其他人一样工作 - 包括期待形式a - > m [b]以某种方式(但那么它如何通过?)。我怀疑这可以做到,因为我相信编写Monad变形金刚将依赖这些函数签名(形式?)是一致的。
即使你没有时间输入一个大的回复,从命令式程序员的角度来看更多文章链接也是天赐之物。
(并对任何不正确的术语使用道歉 - 我也一直在采取行动)
答案 0 :(得分:1)
我认为正在讨论的monad实例确实保留State
结构,并且当它不应该时,元组会抛出一个循环。
如果我们退后一步,询问应该绑定什么签名以符合我们的monad预期,我们得到这个:
(>>=) :: State s a -> (a -> State s b) -> State s b
someState >>= someFuncOfA = ...
并且我会在monadic值构造函数周围添加额外的括号,其中记住的是State s
而不是仅仅是State
,因为具体类型不能成为实例Monad,只是一个参数的类型构造函数:
(>>=) :: [State s] a -> (a -> [State s] b) -> [State s] b
someState >>= someFuncOfA = ...
其中[State s]
将是我们所有monad内容的特殊m
。
由于State
的新类型是
newtype State s a = State s -> (a, s)
我们知道,无论价值构建State
值意味着什么,该值所采用的参数是函数 s -> (a, s)
。
备份我们的不完整绑定定义:
(>>=) :: [State s] a -> (a -> [State s] b) -> [State s] b
someState >>= someFuncOfA = ...
我们知道...
必须State $ (someNewFunc)
导致State
如何获得价值构建。
首先观察:元组与此没有多大关系。它是函数 someNewFunc
,只要它的类型匹配值构造State
所需的,就维持我们想要的monadic结构。 该函数恰好需要类型s -> (a, s)
的事实几乎只是一个实现选择。签名可能有很多不同的东西,因为有not really a difference between tuples and value constructors,我确定你可以先创建一个特殊的data
类型,其值构造函数就像一个二元组为了这个目的,并使签名看起来人为更好。
第二个观察:State
没有导出的'State'
值构造函数,至少不是Control.Monad.State
,所以你使用辅助函数
state :: (s -> (a, s)) -> State s a
再次将函数作为其第一个参数,并在幕后进行值构建。
所以我们知道在绑定定义中,我们无法编写
someState >>= someFuncOfA = State $ (someFunc)
而是需要
someState >>= someFuncOfA = state $ (someFunc)
评估时和state $ (someFunc)
导致类型为State s a
,因为someFunc
被强制使用(s -> (a,s))
类型并且实际上会使用{{1}完成这个。
这可能不是一个很好的写作。我试图将FP Complete State Monad tutorial的相关部分翻译成解决这一特定问题的内容。也许阅读会提供更好的描述。
答案 1 :(得分:0)
重新阅读FPComplete,F先生的回答和另一个关于状态monad的SO回答我终于有了我的时间。
让我感到困惑的是被困在考虑值。有了我已经熟悉的单子,我有这个心理障碍,我一直试图把状态monad想象为一个值增加 - 所以在一定程度上,Mr.F是正确的,因为元组让我感到困惑和不必要的。 / p>
我认为状态monad有两个真正让它点击的东西:
函数式编程中处理状态的方式是将其作为参数之一和返回的一部分进行处理。所以国家monad是关于穿越国家。换句话说,它放大了一些无法将状态线程化为能够线程状态的东西。
如果其中一个类型的值为' a',则启用此状态的方式是使其成为一个函数:s - >是的
状态monad的回归就是这样。例如,如果我的Int值为5,状态monad的返回将给我一个函数:s - > s 5(现在值' 5'可以通过说话的方式将状态穿过它)。
当然 bind 使用的形式为a - > MB。
因为State包含一个函数,它接受一个状态值并返回一个状态值(以及函数的常规返回值!),一个状态感知函数的预期形式是:a - > mb我们已经知道mb是一个可以“解决”问题的函数。州。所以它的行为很像 - > s - >某人
这个结论是任何函数a - > b,或任何价值' a'现在可以被国家单子放大,以获得让国家穿过它的能力。现在它可以很容易地与状态感知的函数链接(绑定)。
这清除了让我烦恼的其他东西:为什么示例Haskell代码(我没有完全理解)必须在最后有这个额外的运行步骤。
一旦所有东西都被放大到线程状态,绑定已被调用,剩下的实际上是一个函数。它链接了所有的a,b,s,c使用bind和fmap最终返回s r(状态和结果)。但它仍然缺少一件事:实际的初始状态。
所以你需要跑步'或者'评估'通过提供初始状态得到的计算结果。
非常感谢FPComplete,F先生和一个很好的答案,我已经丢失了这个链接:)
彩虹和独角兽,直到我决定现在解决Monad变形金刚。