cib*_*en1 27 monads haskell functional-programming
澄清问题:它是关于monad类型类的优点(而不是它没有统一类的实例).
在阅读了许多参考文献(见下文)之后,我得出的结论是,实际上,monad类只能解决一个问题,即大而重要的问题:关于具有上下文的类型的函数的"链接".因此,着名的句子"monad是可编程的分号".实际上,monad可以被视为具有辅助操作的函数数组.
我坚持monad 类之间的区别,被理解为其他类型的通用接口; 以及这些其他类型实例化类(因此,"monadic类型").
我理解monad类本身只解决了运算符的链接,因为主要是它只要求它的类型实例具有bind >>=和return,并且告诉我们它们必须如何表现.作为奖励,编译器非常有助于编码为monadic do类型提供符号.
另一方面,实例化monad类的每个单独类型解决了每个具体问题,但不仅仅是作为Monad的实例.例如,Maybe解决"函数如何返回值或错误",State解决"如何使用全局状态的函数",IO解决"如何与外部世界交互"等等.所有这些类都在上下文中封装了一个值.
但不久之后,我们需要对这些上下文类型进行连锁操作.也就是说,我们需要以特定的顺序组织对这些类型的函数的调用(对于这样一个问题的例子,请阅读你可能已经发明了monad的多值函数的例子).
如果每个类型都是monad类的实例,那么你就解决了链接的问题.为了使链接起作用,你需要>>=具有它所具有的确切签名,而不是其他.(见这个问题).
因此,我猜你下次定义上下文数据类型T来解决问题时,如果你需要对函数的调用进行排序(在T的值上),可以考虑将T作为一个实例Monad(如果你需要"选择链接",如果你可以从do符号中受益).为了确保你做得对,检查T是否符合monad法则
然后,我向Haskell专家提出两个问题:
J. *_*son 22
你肯定会采用某种方式表达你的Monad意思- 有很多事情意味着你把它们分开了.
也就是说,我肯定会说链接操作不是 Monads解决的主要问题.这可以使用简单的Functors(有一些麻烦)或使用Applicatives轻松解决.当"选择链接"时,您需要使用完整的monad规范.特别是,之间的张力Applicative和Monad来自Applicative需要知道副作用的计算的整个结构静态.Monad可以在运行时更改该结构,从而牺牲一些可分析性的功率.
为了更清楚地说明这一点,你处理Monad而不是任何特定 monad 的唯一地方就是你定义的多态性被限制为Monad.这在Control.Monad模块中反复出现,因此我们可以从那里检查一些例子.
sequence :: [m a] -> m [a]
forever :: m a -> m b
foldM :: (a -> b -> m a) -> a -> [b] -> m a
Run Code Online (Sandbox Code Playgroud)
我们可以立即抛出sequence特定的东西,Monad因为它有一个相应的函数Data.Traversable,sequenceA它的类型略大于Applicative f => [f a] -> f [a].这应该是一个明确的指标,Monad不是排序事物的唯一方法.
同样,我们可以定义foreverA如下
foreverA :: Applicative f => f a -> f b
foreverA f = flip const <$> f <*> foreverA f
Run Code Online (Sandbox Code Playgroud)
因此有更多的方法来排序非Monad类型.但是我们遇到了麻烦foldM
foldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
foldM _ a [] = return a
foldM f a (x:xs) = f a x >>= \fax -> foldM f fax xs
Run Code Online (Sandbox Code Playgroud)
如果我们尝试将此定义转换为Applicative样式,我们可能会写
foldA :: (Applicative f) => (a -> b -> f a) -> a -> [b] -> f a
foldA _ a [] = pure a
foldA f a (x:xs) = foldA f <$> f a x <*> xs
Run Code Online (Sandbox Code Playgroud)
但是Haskell会理所当然地抱怨这不是类型检查 - 每次递归调用foldA试图f在结果上放置另一个"层" .随着Monad我们可以join这些层下降,但Applicative太弱.
那么这又如何转化为Applicative限制我们从运行时选择?嗯,这正是我们所表达的foldM,一个monadic计算(a -> b -> m a)取决于它的a参数,一个先前的monadic计算的结果.这种事情在更纯粹的连续世界中没有任何意义Applicative.
为了解决在单个monadic类型上链接操作的问题,根本不需要使它成为一个实例Monad并确保monad定律得到满足.您可以直接在您的类型上实现链接操作.
它可能与monadic绑定非常相似,但不一定完全相同(回想一下,列表的绑定是concatMap一个无论如何都存在的函数,但是参数的顺序不同).而且您不必担心monad定律,因为每种类型的接口都会略有不同,因此它们没有任何共同的要求.
要询问Monad类型类本身解决了什么问题,请查看Control.Monad处理任何 monadic类型的值的所有函数(in 和else where).解决的问题是代码重用!Monad正是所有monadic类型的一部分,每个类型都是每个类型的共同点.该部分足以编写有用的计算.所有这些功能都可以对任何个人一元型(往往更直接)来实现,但他们已经被用于实现所有单子的类型,甚至还不存在的人.
您不编写Monad实例,以便可以对类型进行链接操作(实际上,您通常已经有了链接方式).您Monad为自动随实例出现的所有代码编写Monad实例.Monad它不是针对任何单一类型解决任何问题,而是关于将许多不同类型视为单个统一概念的实例.