1

我的理解是,对于 do monad,每一步都有一个延续和一个结束。

这位作者写道

我们已经看到纯度、强类型和 monad 可以:

...

  • 防止由于不同执行阶段之间的混淆而可能出现的错误。

我的问题是:monads 防止的错误类别是什么?

4

2 回答 2

0

假设您编写了一个必须接收回调的算法。你不知道回调想要做什么,或者它能做什么。接受此类回调的最通用方法是接收如下函数:

Monad m => a -> m b

这给了你的调用者完全的自由(他可以选择任何m一个 Monad),同时拒绝你的库的任何这样的自由。这可以防止在其他纯库中引入副作用,同时允许在调用者需要时发生副作用。

我以前在纯寄存器分配器中使用过这种模式。在那个库中,我自己从来不需要效果,但希望允许用户使用他们自己的效果(例如状态)来创建新块和移动指令。

于 2015-08-06T19:22:46.187 回答
0

效果分离

就像普通类型给你一种区分数据的方法一样,monad 给你一种区分效果的方法。

灵药

这是 中的一个示例Elixir,它是基于 Erlang 的近乎纯函数式语言。这个例子来源于我工作中经常发生的真实情况。

def handle_call(:get_config_foo, _, state) do:
  {:reply, state.foo, state}
end

def handle_call(:get_bar, _, state) do:
  {:reply, state.bar, state}
end

def handle_call({:set_bar, bar}, _, state) do:
  {:reply, :ok, %{state | bar: bar}}
end

这定义了GenServer的 API ,它是一个小的 Erlang 节点,其中包含一些state并允许您查询和更改它。第一次调用,:get_config_foo读取一个不可变的配置设置。第二组调用:get_barand{:set_bar, bar}获取并设置一个可变状态变量

我多么希望我在这里有单子,以防止以下错误:

def handle_call({:set_bar, bar}, _, state) do:
  {:reply, :ok, %{state | foo: bar}}
end

你能发现错误吗?好吧,我只是写了一个只读值。Elixir 中没有任何东西可以阻止这一点。您不能将 GenServer 状态的某些部分标记为只读,而其他部分则为读写。

Haskell:读者和状态

在 Haskell 中,您可以使用不同的 monad 来指定不同类型的效果。以下是只读状态(Reader)和读写状态:

data Reader r a = Reader (r -> a)
data State s a = State (s -> (a, s))

Reader允许您访问配置状态r以返回一些值aState允许您读取状态、返回一些值修改状态。两者都是 monad,这本质上意味着您可以以命令式的方式顺序链接这些状态访问。您可以先读取一个配置设置,Reader然后(基于第一个设置)读取另一个设置。在State中,您可以读取状态,然后(根据您读取的内容)以进一步的方式对其进行修改。但是你永远不能Reader在你执行它的时候修改它的状态。

确定性影响

让我重复一遍。绑定多个调用以Reader确保您永远无法修改其间的读取器状态。如果你有getConfigFoo1 :: Reader Config Foo1并且getConfigFoo2 :: Foo1 -> Reader Config Foo2你有getAllConfig = getConfigFoo1 >>= getConfigFoo2,那么你可以确定这两个查询将在相同的情况下运行Config。Elixir 没有此功能,并且可以忽略上述错误。

其他有用的效果是Writer(只写状态,例如日志记录)和Either(异常处理)。当你有Writer而不是ReaderorState时,你可以确定你的状态只会被附加到。当您拥有 时Either,您就可以确切地知道可能发生的异常类型。IO这一切都比用于日志记录和异常处理要好得多。

于 2019-02-25T14:44:54.147 回答