- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
考虑以下示例。
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
这是
Right [7]
.
Right [5,7,1]
因为
handler
通过不重新抛出异常从异常中恢复。本质上,他们预计
catchError
搬进
ex1
如下。
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) `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
事实上,这就是可扩展效应的作用。它们通过将效果处理程序移近效果源来更改程序的语义。例如,
local
移近
ask
和
catchError
移近
throwError
.该论文的作者将此称为可扩展效果优于 monad 转换器的优势之一,声称 monad 转换器具有“不灵活的语义”。
Right [7]
怎么办?而不是
Right [5,7,1]
无论出于什么原因?如上面的示例所示,可以使用 monad 转换器来获得这两种结果。然而,因为可扩展的效果似乎总是将效果处理程序移近效果源,所以似乎不可能得到结果
Right [7]
.
最佳答案
我也对那篇特定论文的摘录中的细微差别感到有些困惑。我认为退后几步并解释该论文所属的代数效应企业背后的动机更有用。
MTL 方法在某种意义上是最明显和最通用的:你有一个接口(interface)(或“效果”),把它放在一个类型类中并称之为一天。这种通用性的代价是它是无原则的:你不知道将接口(interface)组合在一起会发生什么。当您实现一个接口(interface)时,这个问题最具体地出现:您必须同时实现所有这些。我们喜欢认为每个接口(interface)都可以在专用转换器中单独实现,但如果您有两个接口(interface),比如 MonadPlus
和 MonadError
,由转换器实现 ListT
和 ExceptT
,为了组合它们,您还必须实现 MonadError
为 ListT
或 MonadPlus
为 ExceptT
.这个 O(n^2) 实例问题通常被理解为“只是样板”,但更深层次的问题是,如果我们允许接口(interface)具有任何形状,那么不知道在该“样板”中可能隐藏什么危险,如果它甚至可以实现。
我们必须在这些接口(interface)上放置更多结构。对于“提升”的某些定义(来自 lift
的 MonadTrans
),我们可以通过变压器均匀提升的效应正是代数效应。 (另见,Monad Transformers and Modular Algebraic Effects, What Binds Them Together。)
这并不是真正的限制。虽然有些接口(interface)在技术意义上不是代数的,例如 MonadError
(因为 catch
),它们通常仍然可以在代数效应的框架内表达,只是不太像字面意思。在限制“接口(interface)”定义的同时,我们也获得了更丰富的使用方式。
所以我认为代数效应首先是一种不同的、更精确的界面思考方式。作为一种思维方式,因此可以在不更改代码的任何内容的情况下采用它,这就是为什么比较往往会查看相同的代码两次,并且如果不了解周围的上下文和动机就很难看出重点。如果您认为 O(n^2) 实例问题是一个微不足道的“样板”问题,那么您已经相信接口(interface)应该是可组合的原则;代数效应是围绕该原则明确设计库和语言的一种方式。
“代数效应”是一个没有固定定义的模糊概念。如今,它们最容易通过以 call
为特色的语法来识别。和一个 handle
构造(或 op
/perform
/throw
/raise
和 catch
/match
)。 call
是使用接口(interface)和 handle
的一种构造是我们如何实现它们。这些语言的共同想法是,有方程(因此是“代数”)提供了如何 call
的基本直觉。和 handle
以一种独立于界面的方式运行,特别是通过 handle
的交互具有顺序组合(>>=)
.
从语义上讲,程序的含义可以用 call
树表示。 s 和 handle
是这种树的改造。这就是为什么 Haskell 中许多“代数效应”的化身都是从自由 monads 开始的,树的类型由节点类型参数化 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
和每个效果
[]
和
Const Int
对应于转换树的某种方式,从树中消除这种影响(可能引入其他人,包括它自己)。
Const
转换效果Free (Const x)
节点进入一些新树 h x
.[]
效果,一种方法是组合 Free [...]
的所有子级节点使用 (>>=)
,将他们的结果收集到最终列表中。这可以看作是深度优先搜索的推广。[7]
或
[5,7,1]
取决于这些转换的排序方式。
MonadError e
为
ListT
.这种直觉在后验上可能有意义,但它是先验混淆的,因为类型类实例不是像处理程序那样的一流值,并且 monad 转换器通常根据最终解释来表示(隐藏在 monad
m
中,它们会转换)而不是初始语法。
关于haskell - 如何使用可扩展效果获得 “inflexible semantics of monad transformers”?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65590600/
在 Haskell 中,类型声明使用双冒号,即 (::),如 not::Bool -> Bool。 但是在许多语法与 Haskell 类似的语言中,例如榆树、 Agda 、他们使用单个冒号(:)来声明
insertST :: StateDecoder -> SomeState -> Update SomeState SomeThing insertST stDecoder st = ... Stat
如果这个问题有点含糊,请提前道歉。这是一些周末白日梦的结果。 借助 Haskell 出色的类型系统,将数学(尤其是代数)结构表达为类型类是非常令人愉快的。我的意思是,看看 numeric-prelud
我有需要每 5 分钟执行一次的小程序。 目前,我有执行该任务的 shell 脚本,但我想通过 CLI 中的键为用户提供无需其他脚本即可运行它的能力。 实现这一目标的最佳方法是什么? 最佳答案 我想你会
RWH 面世已经有一段时间了(将近 3 年)。在在线跟踪这本书的渐进式写作之后,我渴望获得我的副本(我认为这是写书的最佳方式之一。)在所有相当学术性的论文中,作为一个 haskell 学生,读起来多么
一个经典的编程练习是用 Lisp/Scheme 编写一个 Lisp/Scheme 解释器。可以利用完整语言的力量来为该语言的子集生成解释器。 Haskell 有类似的练习吗?我想使用 Haskell
以下摘自' Learn You a Haskell ' 表示 f 在函数中用作“值的类型”。 这是什么意思?即“值的类型”是什么意思? Int 是“值的类型”,对吗?但是 Maybe 不是“值的类型”
现在我正在尝试创建一个基本函数,用于删除句子中的所有空格或逗号。 stringToIntList :: [Char] -> [Char] stringToIntList inpt = [ a | a
我是 Haskell 的新手,对模式匹配有疑问。这是代码的高度简化版本: data Value = MyBool Bool | MyInt Integer codeDuplicate1 :: Valu
如何解释这个表达式? :t (+) (+3) (*100) 自 和 具有相同的优先级并且是左结合的。我认为这与 ((+) (+3)) (*100) 相同.但是,我不知道它的作用。在 Learn
这怎么行 > (* 30) 4 120 但这不是 > * 30 40 error: parse error on input ‘*’ 最佳答案 (* 30) 是一个 section,它仍然将 * 视为
我想创建一个函数,删除满足第二个参数中给定谓词的第一个元素。像这样: removeFirst "abab" ( 'b') = "abab" removeFirst [1,2,3,4] even =
Context : def fib(n): if n aand returns a memoized version of the same function. The trick is t
我明白惰性求值是什么,它是如何工作的以及它有什么优势,但是你能解释一下 Haskell 中什么是严格求值吗?我似乎找不到太多关于它的信息,因为惰性评估是最著名的。 他们各自的优势是什么。什么时候真正使
digits :: Int -> [Int] digits n = reverse (x) where x | n digits 1234 = [3,1,2,4]
我在 F# 中有以下代码(来自一本书) open System.Collections.Generic type Table = abstract Item : 'T -> 'U with ge
我对 Haskell 比较陌生,过去几周一直在尝试学习它,但一直停留在过滤器和谓词上,我希望能得到帮助以帮助理解。 我遇到了一个问题,我有一个元组列表。每个元组包含一个 (songName, song
我是 haskell 的初学者,我试图为埃拉托色尼筛法定义一个简单的函数,但它说错误: • Couldn't match expected type ‘Bool -> Bool’
我是 Haskell 语言的新手,我在使用 read 函数时遇到了一些问题。准确地说,我的理解是: read "8.2" + 3.8 应该返回 12.0,因为我们希望返回与第二个成员相同的类型。我真正
当我尝试使用真实项目来驱动它来学习 Haskell 时,我遇到了以下定义。我不明白每个参数前面的感叹号是什么意思,我的书上好像也没有提到。 data MidiMessage = MidiMessage
我是一名优秀的程序员,十分优秀!