monad可以防止哪类错误

  • 本文关键字:错误 monad monads defects
  • 更新时间 :
  • 英文 :


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

本文作者写道:

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

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

我的问题是:monad可以防止哪类bug

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

Monad m => a -> m b

这给了您的调用者完全的自由(他可以选择任何属于Monad的m),同时拒绝了您的库的任何此类自由。这可以防止在纯库中引入副作用,同时允许在调用方需要时出现副作用。

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

效果分离

与普通类型一样,monad为您提供了区分数据的方法,也为您区分effect提供了方法。

长生不老药

下面是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_bar{:set_bar, bar}获取并设置一个可变状态变量

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

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

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

哈斯克尔:读者与状态

在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 Foo1getConfigFoo2 :: Foo1 -> Reader Config Foo2,并且有getAllConfig = getConfigFoo1 >>= getConfigFoo2,则可以确定这两个查询将在同一个Config上运行。Elixir没有这个功能,并允许上面提到的错误被忽视。

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

相关内容

  • 没有找到相关文章

最新更新