【问题标题】:Intuition for "run" function of monads对单子“运行”功能的直觉
【发布时间】:2019-02-05 13:27:05
【问题描述】:

我正在学习 Haskell 中的 monad,我了解它们为何有用,我大致了解 bind、join、return 的作用。

我还查看了基本读取器/写入器/状态/列表/​​可能是单子的基本用法示例。

尽管如此,作为一个初学者,我仍然不知道“运行”函数(例如 runState、runReader、runWriter)的一般含义。它似乎没有像上述函数那样的通用签名,如果它是可定义的/对所有单子都有意义,我不明白。

【问题讨论】:

  • 如果你的意思是runStaterunReader 等函数,那么不,这些函数并不适用于所有 monad。
  • 各种run*函数采用由>>=链接在一起的函数“链”,并返回最终按顺序计算所有函数产生的值。
  • “通用”签名可以看作是一个T-代数;获取一元值并“提取”包装代码在最终评估时产生的任何值。 >>= 通常除了构建一个更大的延迟代码链之外什么都不做。
  • 通常,run* 函数是无操作的,只是暴露了 monad 的内部表示。例如,Reader r ar -> a 的包装器,runReader 将前者转换为后者。这同样适用于包裹(w, a)Writer w a 和包裹s -> (s, a)State s a。在构建了一个(可能又长又复杂的)monadic 计算之后,使用run* 最终让该计算与不在同一个 monad 下的程序的其余部分交互。

标签: haskell functional-programming monads


【解决方案1】:

大多数 monad 的 run 函数实际上只是 monad 如何在内部表示的工件;例如,Reader monad 理论上可以表示为

type Reader r a = r -> a

State作为

type State s a = s -> (s, a)

等等。但是,如果我们这样做了,我们就无法为 ReaderState 提供不同的类型类(包括 Monad)实现,因为它们都只能由 (->) 表示。

--也就是说,如果我们写了

instance Functor (Reader r)
  -- ....

instance Functor (State s)
  -- ...

我们的编译器会抱怨我们试图为(->) a 提供两个不同的Functor 实现。

因此,我们或多或少地用newtype 编写相同的东西,而不是type,例如

newtype Reader r a = Reader { runReader :: r -> a }

newtype State s a = State { runState :: s -> (s, a)}

如您所见,run 函数在这里实际上没有任何事情,它们只是“解开”新类型,因此我们可以获得底层值。

(实际实现可能涉及 monad 转换器,因此看起来有点复杂,但它们本质上仍在做同样的事情)。

【讨论】:

    猜你喜欢
    • 2020-10-03
    • 2021-12-05
    • 2021-12-28
    • 2011-05-30
    • 1970-01-01
    • 2010-09-21
    • 2020-06-06
    • 1970-01-01
    相关资源
    最近更新 更多