有人可以提供一个超级简单(几行)的monad变换器示例,这是非平凡的(即不使用Identity monad - 我理解) .
例如,有人会如何创建一个执行IO并可以处理失败的monad(可能)?
什么是最简单的例子来证明这一点?
我已经浏览了一些monad变换器教程,他们似乎都使用State Monad或Parsers或者复杂的东西(对于newbee) . 我想看到一些比这简单的东西 . 我认为IO也许很简单,但我自己并不知道怎么做 .
我怎么能使用IO Maybe monad堆栈?最重要的是什么?什么会在底部?为什么?
在什么样的用例中,人们想要使用IO Maybe monad还是Maybe IO monad?创造这样一个复合单子会有意义吗?如果是,何时以及为什么?
3 回答
这可用here作为.lhs文件 .
MaybeT
变换器将允许我们打破monad计算,就像抛出异常一样 .我会先快点一些预赛 . 跳到 Adding Maybe powers to IO 以获得一个有效的例子 .
首先进口一些:
经验法则:
其他类似IO的monad通常也会出现在底部,例如:国家变压器monad
ST
.我们将进入后期的力量 . 现在,习惯于将
MaybeT IO
视为可能的IO monad堆栈 .习惯阅读复合型签名是了解monad变形金刚的一半 .
即这是有效的,因为每个语句都在IO-monad中:
这不起作用,因为
putStr
不在MaybeT IO
monad中:幸运的是,有一种方法可以解决这个问题 .
liftIO
是多态的,但在我们的例子中它有类型:现在
mgreet
中的所有语句都来自MaybeT IO
monad .run函数“运行”monad堆栈的最顶层,从内层返回一个值 .
对于
MaybeT IO
,运行函数为:例:
也尝试运行:
你需要使用Ctrl-C来摆脱循环 .
到目前为止
mgreet
没有't do anything more than what we could do in IO. Now we' ll工作一个例子,它展示了将Maybe monad与IO混合的强大功能 .将IO添加到IO
我们将从一个提出一些问题的程序开始:
现在假设我们希望通过在回答问题时输入END来让用户能够提前结束调查 . 我们可以这样处理它:
问题是
survey1
有熟悉的阶梯问题,如果我们添加更多问题,这个问题就无法扩展 .我们可以使用MaybeT monad变换器来帮助我们 .
注意
askfor2
中的所有状态都具有相同的monad类型 .我们使用了一个新功能:
以下是这些类型的解决方法:
这里
return
来自IO-monad .现在我们可以像这样编写我们的调查函数:
尝试运行
survey2
并通过键入END作为对任一问题的回复来提前结束问题 .捷径
我知道如果我不提及以下捷径,我会得到人们的评论 .
表达方式:
也可以简单地写成:
另外,编写
MaybeT (return Nothing)
的另一种方法是:此外,两个连续的
liftIO
语句可能总是组合成一个liftIO
,例如:是相同的:
通过这些更改,我们可以编写
askfor2
函数:从某种意义上说,
mzero
成为一种打破monad的方式 - 就像抛出异常一样 .另一个例子
考虑这个简单的密码问循环:
这是一个(尾部)递归函数,工作得很好 .
在传统语言中,我们可以将其写为带有break语句的无限while循环:
使用MaybeT,我们可以像Python代码一样编写循环:
最后
return ()
继续执行,因为我们是在forever
循环中,控件传递回do块的顶部 . 请注意,loop2
可以返回的唯一值是Nothing
,这对应于断开循环 .根据情况,您可能会发现编写
loop2
而不是递归loop1
更容易 .假设你必须使用
IO
值"may fail"在某种意义上,如foo :: IO (Maybe a)
,func1 :: a -> IO (Maybe b)
和func2 :: b -> IO (Maybe c)
.手动检查一系列绑定中是否存在错误会产生可怕的“厄运的阶梯”:
如何以某种方式"automate"?也许我们可以使用绑定函数在
IO (Maybe a)
周围设计一个新类型,该函数会自动检查第一个参数是否在IO
内是Nothing
,这样我们就省去了检查它的麻烦 . 就像是使用绑定功能:
这有效!而且,仔细观察它,我们意识到我们没有使用
IO
monad独有的任何特定功能 . 稍微概括一下newtype,我们可以为任何潜在的monad做这个工作!从本质上讲,这就是
MaybeT
变压器works . 我遗漏了一些细节,比如如何为变压器实现return
,以及如何将"lift"IO
值转换为MaybeOverM IO
值 .请注意
MaybeOverIO
有种* -> *
而MaybeOverM
有种(* -> *) -> * -> *
(因为它的第一个"type argument"是monad类型构造函数,它本身需要"type argument") .当然,
MaybeT
monad变压器是:我们可以这样实现它的monad实例:
这将允许我们执行IO,在某些情况下可以选择优雅地失败 .
例如,假设我们有这样的函数:
我们可以使用该函数的结果独立地操作monad,但是如果我们这样组成它:
我们可以忘记那个额外的monadic层,并将它视为一个普通的monad .