究竟做了什么"有效"意味着

时间:2015-10-28 08:55:34

标签: haskell monads applicative side-effects

我一次又一次读到有效这个词,但我仍然无法清楚地定义它的含义。我假设正确的上下文是有效的计算,但我也看到了有效的 values 这个词

我曾经认为有效意味着有副作用。但在Haskell中没有副作用(除了某种程度上IO)。整个地方仍有有效的计算。

然后我读到monad用于创建有效的计算。我可以在State Monad的背景下对此有所了解。但我没有看到Maybe monad中的任何副作用。一般来说,在我看来,Monads包含一个类似函数的东西比Monads更容易看到产生副作用,Monads只包装一个值。

谈到Applicative仿函数,我更加迷失。我总是看到applicative functor作为map一个带有多个参数的函数的方法。我在这里看不到任何副作用。或效果与效果之间有区别吗?

4 个答案:

答案 0 :(得分:22)

side effect是与其环境的可观察交互(除了计算其结果值)。在Haskell中,我们努力避免具有这种副作用的函数。这甚至适用于IO操作:当评估IO操作时,不会执行任何副作用,只有在IO值中规定的操作在{{1}内执行时才执行它们}}

然而,当处理与组合计算有关的抽象时,例如应用仿函数和monad,可以方便地区分实际值和休息"我们经常呼叫"效果"。特别是,如果我们的main类型为kind f,那么在* -> *中,f a部分为"值为"什么"遗骸"是"效果"。

我故意引用这些术语,因为没有确切的定义(据我所知),它只是一个口语定义。在某些情况下,根本没有值或多个值。例如a"效果"是Maybe"效果"可能没有值(计算被中止)是有多个(或零)值。对于更复杂的类型,这种区别可能更加困难。

"效果之间的区别"和"价值观"并不真正依赖于抽象。 []FunctorApplicative只是向我们提供了我们可以用它们做的工具(Monad允许修改内部值,Functor s允许组合效果并且Applicative允许效果取决于之前的值)。但是在Monad s的背景下,创建一个正在发生的事情的心理图片会更容易一些,因为monadic动作可以"看到"上一次计算的结果值,如

所示
Monad

运算符:第二个函数接收类型(>>=) :: m a -> (a -> m b) -> m b 的值,因此我们可以想象"先前的计算有一些效果,现在有结果值,我们可以用它来做某事"。

答案 1 :(得分:9)

为了支持Petr Pudlák's answer,这里有一个论点,关于那里所支持的“效果”这个更广泛概念的起源。

“有效编程”这一短语出现在McBride和Patterson的Applicative Programming with Effects摘要中,该论文介绍了应用函子:

  

在本文中,我们介绍了Applicative仿函数 - 一种有效编程的应用风格的抽象表征,比Monad弱,因此更广泛。

“效果”和“有效”出现在该论文的其他一些段落中;这些事件被认为是不起眼的,不需要明确澄清。例如,在Applicative的定义出现之后(第3页):

之后发表了这样的评论
  

在每个示例中,都有一个嵌入通常的类型构造函数f   价值观,但支持其自己特殊的方式赋予通常的应用语言[...]我们相应地介绍Applicative类:

     

[Haskell对Applicative]的定义

     

此课程概括 S K [即显示在Reader / function Applicative实例中的the S and K combinators从一个环境线程化到一般的线程效果。

从这些引用中,我们可以推断出,在这种背景下:

  • 效果是Applicative线程“一般”的事情。

  • 效果与给定Applicative个实例的类型构造函数相关联。

  • Monad也处理效果。

根据这些线索,我们可以将“效果”的这种用法追溯到至少Wadler's papers on monads。例如,以下是 Monads的第6页引用函数式编程

  

通常, a→b 类型的函数被类型 a的函数替换   →M b 。这可以作为接受 a 类型的参数的函数读取   并返回 b 类型的结果,并捕获可能的附加效果   中号。这种效果可能是对状态,生成输出,引发异常或者你有什么行为。

从同一篇论文,第21页:

  

如果monads封装效果并且列表形成monad,那么列表是否对应于某些效果?确实他们这样做,他们所对应的效果就是选择。可以将 [a] 类型的计算视为提供值的选择,每个元素对应一个列表。 a→b 类型函数的monadic等效函数是 a→[b] 类型的函数。

这里的“对应一些效果”这句话是关键。它与摘要中更直接的主张有关:

  

Monads为模拟其他语言中的效果提供了一个方便的框架,例如全局状态,异常处理,输出或非确定性。

音调是monad可以用来表达那些在“其他语言”中通常被编码为副作用的东西 - 就像PetrPudlák在这里所说的那样,“与[的可观察的交互]一个函数的环境(除了计算其结果值)“。通过转喻,这很容易导致“效果”获得第二个含义,比“副作用”更广泛 - 即通过类型构造函数引入的任何内容,即Monad实例。随着时间的推移,这个含义进一步推广到涵盖其他仿函数类,如Applicative,如McBride和Patterson的作品中所见。

总之,我认为“效果”在Haskell的说法中有两个合理的含义:

  • “文字”或“绝对”的:效果是副作用;以及

  • “一般化”或“相对”一种:效果是一种函数上下文。

有时,当涉及的每一方隐含地假设“效果”的不同含义时,就会发生可避免的术语分歧。另一个可能的争论点涉及单独处理Functor时是否合理地谈论效果,而不是像ApplicativeMonad这样的子类(我认为这样做是可以的) ,同意Petr Pudlák's answer to Why can applicative functors have side effects, but functors can't?)。

答案 2 :(得分:4)

在我看来,“副作用”是正常功能无法做到的任何事情。换句话说,除了只返回一个值之外的任何东西。

考虑以下代码块:

let
  y = foo x
  z = bar y
in foobar z

这会调用foo,然后调用bar,然后调用foobar三个普通函数。很简单吧?现在考虑一下:

do
  y <- foo x
  z <- bar y
  foobar z

这也调用了三个函数,但它也在每对行之间无形地调用(>>=)。这意味着会发生一些奇怪的事情,具体取决于函数运行的monad类型。

  • 如果这是身份monad,没有什么特别的事情发生。 monadic版本与纯版本完全相同。没有副作用。

  • 如果每个函数都返回Maybe - 那么,如果(例如)bar返回Nothing,则整个代码块将中止。正常功能不能这样做。 (即,在纯版本中,无法阻止foobar被调用。)所以这个版本做了纯版本不能做的事情。每个函数都可以返回值或中止块。这是副作用。

  • 如果每个函数返回一个列表,那么代码将针对所有可能的结果组合执行。同样,在纯版本中,无法使任何函数使用不同的参数执行多次。这就是副作用。

  • 如果每个函数都在状态monad中运行,那么(例如)foo可以将一些数据直接发送到foobar,除了您可以看到传递的值bar 1}}。同样,你不能用纯函数做到这一点,所以这是一个副作用。

  • IO monad中,你有各种有趣的效果。您可以将文件保存到磁盘(文件基本上是一个巨大的全局变量),甚至可以影响在其他计算机上运行的代码(我们称之为网络I / O)。

  • ST monad是IO monad的缩减版。它允许可变状态,但自包含的计算不能相互影响。

  • STM monad允许多个线程相互通信,并且可能导致代码多次执行,并且......好吧,你不能用正常的函数做任何这些。< / p>

  • 延续monad允许你打破人们的思想!可以说纯粹的功能可以实现......

答案 3 :(得分:0)

“效果是一个非常模糊的术语,这是可以的,因为我们正在尝试谈论语言之外的东西。效果和副作用不是同一回事。效果是好的。副作用是错误。

他们的词汇相似性确实是不幸的,因为它导致许多人在阅读这些思想时混淆了这些思想,并且人们使用一种思想代替了另一种思想,因此引起了很多困惑。”

更多信息,请参见此处:https://www.slideshare.net/pjschwarz/rob-norrisfunctionalprogrammingwitheffects