如何使用可扩展效果获得“单子转换器的不灵活语义”?

如何解决如何使用可扩展效果获得“单子转换器的不灵活语义”?

考虑以下示例。

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 return x

ex3 :: Either TooBig [Int]
ex3 = runIdentity . runExceptT . runListT $ ex2

ex3 的值应该是多少?如果我们使用 MTL,那么答案是 Right [7],这是有道理的,因为 ex1 因抛出错误而终止,而 handler 仅返回纯值 return 7,即 { {1}}。

然而,在论文 “Extensible Effects: An Alternative to Monad Transformers” by Oleg Kiselyov,et al. 中,作者说这是“一个令人惊讶且不受欢迎的结果”。他们期望结果是 Right [7],因为 Right [5,1] 通过不重新抛出异常从异常中恢复。本质上,他们希望 handler 移动到 catchError 中,如下所示。

ex1

确实,这就是可扩展效果的作用。它们通过将效果处理程序移近效果源来更改程序的语义。例如,newtype TooBig = TooBig Int deriving Show choose :: Monadplus m => [a] -> m a choose = msum . map return ex1 :: (Monadplus m,1] if x > 5 then throwError (TooBig x) `catchError` handler else return x where handler (TooBig x) = if x > 7 then throwError (TooBig x) else return x ex3 :: Either TooBig [Int] ex3 = runIdentity . runExceptT . runListT $ ex1 移近 localask 移近 catchError。该论文的作者将此称为可扩展效果相对于 monad 转换器的优势之一,声称 monad 转换器具有“不灵活的语义”。

但是,如果我希望结果是 throwError 而不是 Right [7] 怎么办?如上面的示例所示,可以使用 monad 转换器来获得这两种结果。但是,由于可扩展效果似乎总是将效果处理程序移近效果源,因此似乎不可能获得结果 Right [5,1]

那么,问题是如何使用可扩展的效果来获得“单子转换器的不灵活语义”?在使用可扩展效果时,是否可以防止单个效果处理程序靠近效果源?如果不是,那么这是需要解决的可扩展效应的限制吗?

解决方法

我也对那篇特定论文的摘录中的细微差别感到有些困惑。我认为这是更加有用采取退后几步,并解释代数影响了企业,到该文件属于背后的动机。

在MTL的做法在某种意义上是最明显和最普遍:你有一个接口(或“效应”),把它放在一个类型的类和收工。即一般性的成本,这是无原则的:你不知道当你把界面在一起会发生什么。这个问题表现得最具体,当你实现一个接口:你必须同时实现所有的人。我们这样想,每个接口都可以在隔离在一个专用的变压器来实现,但如果你有两个接口,比如说MonadPlusMonadError,通过变压器实现ListT和{{1} },为了撰写他们,你也必须要么执行ExceptT的{​​{1}}或MonadError的{​​{1}}。这为O(n ^ 2)实例问题被普遍理解为“只是样板”,但更深层次的问题是,如果我们允许接口是任何形状的,有没有讲述什么危险可能隐藏在“样板”,如果甚至可以在所有来实现。

我们必须在这些接口上放置更多结构。对于“升降机”的一些定义(ListTMonadPlus),是我们可以通过变压器均匀地解除作用完全代数影响。 (也参见,Monad Transformers and Modular Algebraic Effects,What Binds Them Together。)

这是不是真正的限制。虽然一些接口不是在技术意义上的代数,如ExceptT(由于lift),它们通常还是可以的代数作用的框架内表达,只是少字面上。同时限制一个“接口”的定义中,我们也获得使用它们的丰富的方式。

所以我认为代数效果是所有之前的接口不同的思维,更精确的方式。作为一种思维方式,因此它可以在不改变你的代码,这就是为什么比较倾向于看相同的代码两次,这是很难看到这一点,而对周围的背景和动机把握什么采纳。如果您想为O(n ^ 2)情况下的问题是一个微不足道的“样板”的问题,你已经相信的原则是接口应该是组合的;代数效果是明确的设计围绕这一原则图书馆和语言的一种方式。

“代数效应”是没有固定定义一个模糊概念。如今它们是最知名的由语法设有MonadTransMonadError构建体(或catch / call / handle / op和{{ 1}} / perform)。 throw是一个结构使用接口和raise是我们如何实现它们。这个想法常见于这种语言是,有式(因此称为“代数”),提供一个基本的直觉如何catchmatch的行为的方式,是独立于所述接口的,特别是通孔的相互作用call 具有顺序组合 handle

在语义上,一个程序的含义可以由call是个树表示,并且一个handle是这样的树的变换。这就是为什么 Haskell 中许多“代数效应”的化身都是从自由 monad 开始的,这些树类型由节点类型 handle 参数化:

(>>=)

从这个角度来看,程序call是具有三个分支的树,与分支标记handle在异常结束:

f

和每个的效果data Free f a = Pure a | Free (f (Free f a)) ex2对应于变换所述树中,消除来自树效应(可能引入其他人,包括自身)的一些方法。

  • 通过将7节点分成一些新的树 “捉” 的异常对应于处理ex2 :: Free ([] :+: Const Int) Int -- The functor "Const e" models exceptions (the equivalent of "MonadError e") ex2 = Free [Pure 5,Free (Const 7),Pure 1] -- You can write this with do notation to look like the original ex2,I'd say "it's just notation". -- NB: constructors for (:+:) omitted {效果{1}}。

  • 要处理[]的影响,方法之一是使用组成一个Const Int节点的所有子Const,收集在一个最终列表他们的结果。这可以被看作是深度优先搜索的概括。

您得到的结果Free (Const x)h x取决于这些转换的排序方式。

当然,在 MTL 方法中,monad 变换器的两个阶数是有对应关系的,但是程序作为树的直觉,通常适用于所有代数效应,当你在中间时就不那么明显了实施实例如[]Free [...]。直觉可能是有意义后验,但它是先验混淆,因为型类的实例是像处理程序不一流值,和单子变压器来讲通常表示最后解释(隐藏在单子(>>=)它们变换),而不是最初的语法。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?