从这个线程(Control.Monad.Cont fun,2005),Tomasz Zielonka介绍了一个函数(由ThomasJäger以清晰而美观的方式评论).Tomasz接受callCC主体的参数(函数)并返回它以供以后使用,具有以下两个定义:
import Control.Monad.Cont
...
getCC :: MonadCont m => m (m a)
getCC = callCC (\c -> let x = c x in return x)
getCC' :: MonadCont m => a -> m (a, a -> m b)
getCC' x0 = callCC (\c -> let f x = c (x, f) in return (x0, f))
Run Code Online (Sandbox Code Playgroud)
这些也在Haskellwiki中提到过.使用它们,你可以像haskell中的goto语义看起来非常酷:
import Control.Monad.Cont
getCC' :: MonadCont m => a -> m (a, a -> m b)
getCC' x0 = …Run Code Online (Sandbox Code Playgroud) 作为一名数学学生,我在Haskell学习monads时所做的第一件事就是检查它们是否真的是我知道的monad.但后来我了解了monad变换器,而那些似乎并不是类别理论研究的东西.
特别是我希望它们与分配法有关,但它们似乎真正不同:monad变换器预计适用于任意monad,而分配法则是monad和特定其他monad之间的事情.
另外,在看单子变压器的通常实施例中,而MaybeT m构成m与Maybe,StateT m不是的组合物m与State任一次序.
所以我的问题是什么是分类语言中的monad变换器?
Hackage上有许多不同的monad变换器库.有些人似乎比其他人更受关注.仅举几例:mtl(由于某种原因取决于变压器的当前版本),变压器,monadLib,monads-tf,mtlx,contstuff.
应该首选哪一个?为什么?它们的独特之处是什么?性能怎么样?
我在mtl库中查找了一些东西时遇到了RWS Monad及其MonadTransformer.那里没有真正的文档,我想知道这是什么以及它在哪里使用.
我发现RWS是Reader,Writer,State的首字母缩略词,这就是这三个monad变换器的堆栈.我无法弄清楚为什么这比国家本身更好.
我想使用的状态单子转换Scalaz 7通过一个分析器,线程额外的状态,我无法做任何有用的事情而无需编写大量的t m a -> t m b版本m a -> m b的方法.
假设我有一个包含嵌套括号的字符串,其中包含数字:
val input = "((617)((0)(32)))"
Run Code Online (Sandbox Code Playgroud)
我还有一个新的变量名称流(在这种情况下是字符):
val names = Stream('a' to 'z': _*)
Run Code Online (Sandbox Code Playgroud)
我想从流的顶部拉出一个名称,并在解析它时将其分配给每个括号表达式,然后将该名称映射到表示括号内容的字符串,并将嵌套的括号表达式(如果有)替换为他们的名字.
为了使这更具体,这就是我希望输出看起来像上面的示例输入:
val target = Map(
'a' -> "617",
'b' -> "0",
'c' -> "32",
'd' -> "bc",
'e' -> "ad"
)
Run Code Online (Sandbox Code Playgroud)
在给定级别可能存在一串数字或任意多个子表达式,但这两种内容不会在单个括号表达式中混合.
为了简单起见,我们假设名称流永远不会包含重复项或数字,并且它总是包含足够的输入名称.
上面的示例是此Stack Overflow问题中解析问题的略微简化版本 .我用一个大致如下的解决方案回答了这个问题:
import scala.util.parsing.combinator._
class ParenParser(names: Iterator[Char]) extends RegexParsers {
def paren: Parser[List[(Char, String)]] …Run Code Online (Sandbox Code Playgroud) 我试图定义一个API来表示我的程序中的特定类型的过程.
newtype Procedure a = { runProcedure :: ? }
Run Code Online (Sandbox Code Playgroud)
有状态,包括ID到记录的映射:
type ID = Int
data Record = { ... }
type ProcedureState = Map ID Record
Run Code Online (Sandbox Code Playgroud)
有三个基本操作:
-- Declare the current procedure invalid and bail (similar to some definitions of fail for class Monad)
abort :: Procedure ()
-- Get a record from the shared state; abort if the record does not exist.
retrieve :: ID -> Procedure Record
-- Store (or overwrite) a record in the shared state. …Run Code Online (Sandbox Code Playgroud) 我见过这个
ListT是一个不满足monad定律的有缺陷的monad变换器的典型例子.
这可以通过一个简单的例子来证明吗?
编辑:我的想法ListT []有点不对,我错过了文档要求内部monad是可交换的.那么,ListT只是在有这个要求的意义上,或者是否存在另一个问题?(Haskell wiki的例子都使用ListT IO,IO显然不是可交换的.)
说我有一些foo :: Maybe Int,我想绑定它,例如bar :: Int -> MaybeT (Writer String) Int,这是什么惯用的方法呢?
我可以定义自己的liftMaybe函数,然后使用它,如:
let liftMaybe = maybe (fail "Nothing") return in liftMaybe foo >>= bar
Run Code Online (Sandbox Code Playgroud)
但是,是否有更惯用(或至少简洁)的方式呢?
你如何设计和构建你的monadic堆栈?我第一次需要构建一个monadic堆栈(使用变换器)来解决现实问题,但我并不完全确定堆叠变换器的顺序.如你所知,只要计算有点* -> *,基本上任何东西都可以在变换器中扮演内部monad的角色,因此有几个问题:
lift . lift . liftIO [...]?我的直觉是,如果变形金刚得到一些实例(例如MonadReader,MonadIO等,就像大多数变形金刚mtl一样),那么放置变压器的顺序并不重要.我有兴趣听取经验丰富的Haskellers关于最佳实践或经验法则的意见.
forever $ print "Thanks!"
一个.
在许多情况下,我不清楚将两个monad与变压器组合而不是使用两个单独的monad可以获得什么.显然,使用两个独立的monad是一件麻烦事,并且可能涉及到符号内部的符号,但是有些情况下它只是表达不够吗?
一个案例似乎是列表上的StateT:组合monads不能得到正确的类型,如果你通过像Bar这样的monad栈获得正确的类型(其中Bar a =(Reader r(List(Writer w(Identity) a))),它没有做正确的事情.
但是我想更准确地理解monad变压器带来什么,当它们是否必要时,以及为什么.
为了使这个问题更加集中:
(关于库的不同选择,我对特定的实现细节不感兴趣,而是对monad变换器/态射正在添加的一般问题(可能是Haskell独立)的问题,作为通过堆叠一堆monadic类型构造函数来组合效果的替代方法.)
(为了给出一点背景,我是一个语言学家,正在做一个丰富蒙塔古语法的项目 - 简单地输入lambda演算,用于将单词意义组成句子 - 用monad变换器堆栈.理解变换器是否真的在做真的很有帮助对我有用的任何东西.)
谢谢,
鲁本