让我们说我有一个功能
f :: State [Int] Int
Run Code Online (Sandbox Code Playgroud)
和功能:
g :: StateT [Int] IO Int
Run Code Online (Sandbox Code Playgroud)
我想用f在g,并通过他们之间的状态.是否有库函数
StateT (return . runState f)?或者一般来说,给定一个具有相应monad的monad变换器,它有一个库函数吗?
假设我想Option在async工作流程中返回一段时间:
let run =
async {
let! x = doAsyncThing
let! y = doNextAsyncThing x
match y with
| None -> return None
| Some z -> return Some <| f z
}
Run Code Online (Sandbox Code Playgroud)
理想情况下,我会使用FSharpx中的可能计算表达式同时作为异步来避免这样做match.我可以创建一个自定义构建器,但是有没有办法一般地组合两个计算表达式?它可能看起来像这样:
let run =
async {
let! x = doAsyncThing
let! y = doNextAsyncThing x
return! f y
}
Run Code Online (Sandbox Code Playgroud) monads f# asynchronous monad-transformers computation-expression
Monad变换器以所有标准monad(Reader,Writer,State,Cont,List等)而闻名,但这些monad变换器中的每一个都以稍微不同的方式工作.在给定具有monad实例的类型构造函数的定义的情况下,没有用于构造monad变换器的通用方法或公式.因此,不能保证根据某些任意业务要求设计的monad数据类型将具有monad变换器.有没有这样一个明确的例子?
另一个问题解释了两个monad的仿函数组合不一定是monad.另见这个问题.这些例子没有回答当前的问题 - 它们仅仅说明了没有构造单子变压器的一般方法的问题.这些例子表明,给定两个单子M和N,我们有时会发现M(N a)是单子,有时N(M a)是单子,有时也不是单子.但这既没有说明如何为M或N构造monad变换器,也没有表明它是否存在.
另一个问题的答案认为,IO单子不能有一个单子转换,因为如果它有一个IOT,我们可以申请IOT到List,然后抬起空列表(lift [])到生成的单子就必须撤消副作用的进行"早期" IO monad.这个论点是基于IOmonad"实际上执行"可能无法撤消的副作用的想法.但是,IOmonad不是显式类型构造函数.
在明确给出monad类型的每个例子中,可以以某种方式找到monad变换器, - 有时需要一定的独创性.例如,最近发现ListT存在于Haskell库中的变换器以微妙的方式发生错误,但最终通过更改定义来解决问题.ListT
没有变换器的monad的标准示例是monad,例如IO,实际上不是由显式类型构造函数定义的 - IO是库以某种方式在低级别定义的不透明"魔术"类型.很明显,无法将IO纯函数赋予monad实例定义为显式类型构造函数.该IO示例显示,如果我们允许monad实例包含具有不纯副作用的隐藏低级代码,则monad转换器可能无法存在.所以,让我们将注意力限制在使用纯函数实现的monad上.
似乎没有一种算法可以从monad的源代码中自动导出monad变换器.我们甚至知道这总是可能的吗?
为了让我更清楚一个monad的"明确例子"我的意思:假设我声称
type Q u v a = ((u -> (a, Maybe a)) -> v) -> u -> (a, Maybe a)
Run Code Online (Sandbox Code Playgroud)
可以有一个合法的Monad实例相对于所述类型参数 …
提供这个组合两个monad的例子的文档Control.Monad.Trans.Error:
type ErrorWithIO e a = ErrorT e IO a
==> ErrorT (IO (Either e a))
Run Code Online (Sandbox Code Playgroud)
我觉得这是违反直觉的:即使ErrorT是所谓包装 IO,它看起来像该错误的信息已经注入到了IO操作的结果类型.我会期待它
==> ErrorT (Either e (IO a))
Run Code Online (Sandbox Code Playgroud)
基于"包装"一词的通常含义.
为了让事情更加混乱,StateT每个人都有一些:
type MyError e = ErrorT e Identity -- (see footnote)
type StateWithError s e a = StateT s (MyError e) a
==> StateT (s -> ErrorT (Either e (a, s)))
Run Code Online (Sandbox Code Playgroud)
状态类型s已被注入Either的Right侧,但整个Either也被包裹在一个功能.
为了使事情变得更加混乱,如果单子相结合的其他方式:
type …Run Code Online (Sandbox Code Playgroud) 我发现monad变换器的一个问题是需要lift将操作转换为正确的monad.单lift在这里和那里也不错,但有时也有功能类似如下:
fun = do
lift a
lift b
c
lift d
lift e
f
Run Code Online (Sandbox Code Playgroud)
我希望能够这样编写这个函数:
fun = monadInvert $ do
a
b
lift c
d
e
lift f
Run Code Online (Sandbox Code Playgroud)
这会减少lifts 的数量并使代码更清晰.
问题是:对于monads是monadInvert可能的?应该如何创建这个功能?
加分点:定义它monad m的实例MonadIO.
这个问题的标题提到了排列:实际上,我们如何处理monad变换器堆栈的任意排列?
在我看来,Scalaz' NonEmptyList有一个monad实例,所以应该有一个monad变换器(有点类似ListT).那是对的吗?
如果是的话,那里有一个吗?(我在Scalaz 7中找不到一个.)
如果没有,即monad变压器是不可能的或没有任何意义,我将不胜感激任何其他解释为什么不.
考虑以下示例。
newtype TooBig = TooBig Int deriving Show
choose :: MonadPlus m => [a] -> m a
choose = msum . map return
ex1 :: (MonadPlus m, MonadError TooBig m) => m Int
ex1 = do
x <- choose [5,7,1]
if x > 5
then throwError (TooBig x)
else return x
ex2 :: (MonadPlus m, MonadError TooBig m) => m Int
ex2 = ex1 `catchError` handler
where
handler (TooBig x) = if x > 7
then throwError (TooBig x)
else …Run Code Online (Sandbox Code Playgroud) monads haskell functional-programming monad-transformers free-monad
在MonadTrans类中:
class MonadTrans t where
-- | Lift a computation from the argument monad to the constructed monad.
lift :: Monad m => m a -> t m a
Run Code Online (Sandbox Code Playgroud)
为什么不被t m限制为Monad?即,为什么不:
{-# LANGUAGE MultiParamTypeClasses #-}
class Monad (t m) => MonadTrans t m where
lift :: Monad m => m a -> t m a
Run Code Online (Sandbox Code Playgroud)
如果答案是"因为那就是它的方式",那很好 - 这只会让n008感到困惑.
为什么Haskell中有两个不同的Writer类型monad?直觉上,读"严格的作家monad"意味着<>严格,所以日志中没有thunk积累.但是,查看源代码,事实证明并非如此:
-- Lazy Writer
instance (Monoid w, Monad m) => Monad (WriterT w m) where
-- ...
m >>= k = WriterT $ do
~(a, w) <- runWriterT m
~(b, w') <- runWriterT (k a)
return (b, w <> w')
Run Code Online (Sandbox Code Playgroud)
在严格的版本中,模式不是无可辩驳的,即~缺失.所以上面发生的是,m并且k a没有评估,但存储为thunk.在严格版本中,它们被评估以检查它们是否与元组模式匹配,结果被馈送到<>.在这两种情况下,在>>=实际需要产生的值之前不会对其进行评估.所以我理解它的方式是懒惰和严格版本都做同样的事情,除了它们在定义中的不同位置有thunk >>=:lazy产生runWriterTthunks,strict产生<>thunk.
这让我有两个问题:
<>不编写自己的包装器和实例的情况下完成严格吗?无论好坏,Haskell流行的Servant库使得在monad变换器堆栈中运行代码变得很普遍ExceptT err IO.仆人自己的处理程序monad是ExceptT ServantErr IO.正如许多人所说,这是一个有点麻烦的monad工作,因为有多种方法可以展开失败:1)通过IO基地的正常例外,或2)返回Left.
正如Ed Kmett的exceptions图书馆有助于澄清:
基于连续的monad和诸如
ErrorT e IO提供多种故障模式的堆栈是this [MonadMask]类的无效实例.
这非常不方便,因为MonadMask我们可以访问有用的[多态版本] bracket函数来进行资源管理(不会因异常而泄漏资源等).但是在Servant的Handlermonad我们不能使用它.
我对它不是很熟悉,但是有些人说解决方案是使用monad-control它和许多合作伙伴库一样,lifted-base并且lifted-async让你的monad访问资源管理工具bracket(大概这适用于ExceptT err IO和朋友一样?).
然而,似乎monad-control是失去了在社会上赞成,但我不能告诉替代将是什么.即使Snoyman最近的safe-exceptions图书馆使用Kmett的exceptions库也避免了monad-control.
有人可以为像我这样试图为严重的Haskell使用方式的人们澄清当前的故事吗?
resources haskell exception-handling monad-transformers io-monad
monads ×9
haskell ×8
asynchronous ×1
f# ×1
free-monad ×1
io-monad ×1
resources ×1
scala ×1
scalaz ×1
state-monad ×1
typeclass ×1
writer ×1