【问题标题】:State Monad, why not a tuple?状态单子,为什么不是一个元组?
【发布时间】:2010-04-07 20:27:21
【问题描述】:

我刚开始研究 monad(至少我想我有),更具体地说是 state monad,有些人比我聪明得多,所以我可能是这样的带着这个问题。

无论如何,state monad 通常用 M 来实现,就像这样 (F#):

type State<'a, 'state> = State of ('state -> 'a * 'state)

现在我的问题是:你有什么理由不能在这里使用元组吗?除了MonadA&lt;'a, 'b&gt;MonadB&lt;'a, 'b&gt; 之间可能存在的歧义,它们都将成为等效的('a * 'b) 元组。

编辑:为清楚起见添加示例

type StateMonad() =
  member m.Return a = (fun s -> a, s)
  member m.Bind(x, f) = (fun s -> let a, s_ = x s in f a s_)

let state = new StateMonad()
let getState = (fun s -> s, s)
let setState s = (fun _ -> (), s) 
let execute m s = m s |> fst

【问题讨论】:

  • 问题是什么?在哪里使用元组?函数的返回类型是一个元组。
  • 不使用元组代替 State 类型,只返回一个函数而不是 State。

标签: .net f# functional-programming monads


【解决方案1】:

State monad 本质上与 'state -&gt; 'res * 'state 类型一起工作,它表示采用一些 初始状态 并产生结果(连同状态的新值)的计算)。

如果你问我们是否给这种类型一些特殊的名字(例如State&lt;'state, 'res&gt;)是否有任何区别,那么答案是这并不重要。为类型指定一些特殊名称的唯一目的是使代码更具可读性。例如,让我们看一下以下示例的两种可能的类型签名:

let foo n = state {
  let! m = getState()
  do! setState(m + 1)
  return sprintf "Result: %d" (n * m) }

// Using State<'state, 'res> type:
val foo : int -> State<int, string>

// Using the underlying representation:
val foo : int -> int -> int * state

第一个类型签名更清楚地表明我们在某个 monad 中编写函数。第二个示例只是一个接受两个int 值的函数。我认为第一个的主要好处是您可以更轻松地意识到该类型可以从其他单子计算中使用(使用state { ... } 编写)。

但是,正如我已经指出的,这不是技术要求。人们可能会使用这种风格,因为许多 monad 来自 Haskell,其中 monad 与 type 相关联(例如 State&lt;'state, 'res&gt;)而不是 computation builder(例如 @ 987654327@),所以在 Haskell 中为每个 monad 定义一个新类型听起来是个好主意。

【讨论】:

  • 谢谢,是的,我根本不打算使用元组版本,我只是在 IRC (##fsharp @ freenode) 上讨论它,我们都看不到 State 的技术原因类型。
【解决方案2】:

您的示例中的一元值的类型不仅仅是一个元组 - 它是一个返回元组的函数:

'state -> 'res * 'state

如果您要问是否可以只使用'state * 'res 作为一元计算的类型,那么答案是否定的。那是行不通的,因为没有办法(安全地)实现返回操作,它必须具有以下类型签名:

// how would we get a value of type 'state in the implementation?
val return : 'a -> 'state * 'a

【讨论】:

    【解决方案3】:

    啊,是的,如果问题是:我应该使用带有 T 类型数据值的单标签可区分联合,还是应该只使用 T,那么您可以使用任何一个。

    在 Haskell 中,您需要使用带有 monad 的数据标签,因为 Haskell do 语法根据值类型推断 monad 类型(元组表示最多可以是单个 Monad 的实例)。而在 F# 中,计算表达式对于 monad 类型是显式的(例如 state { ... }async { ... } 或其他),因此没有必要进行此限制,但相同的表示类型可以用于多个 monad。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-03-29
      • 2020-11-07
      • 1970-01-01
      • 2022-06-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多