9

我刚刚把我的头绕在单子上(至少我想我想我有),更具体地说是状态单子,有些人比我聪明得多,所以我可能对这个问题有所了解.

无论如何,状态单子通常用 M<'a> 来实现,如下所示(F#):

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

现在我的问题是:你有什么理由不能在这里使用元组吗?MonadA<'a, 'b>除此之外,和之间可能存在的歧义MonadB<'a, 'b>都将成为等价的('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
4

3 回答 3

15

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

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

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 与类型相关联(例如State<'state, 'res>)而不是计算构建器(例如state),因此为每个 monad 定义一个新类型听起来是个好主意哈斯克尔。

于 2010-04-07T21:13:50.030 回答
7

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

'state -> 'res * 'state

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

// how would we get a value of type 'state in the implementation?
val return : 'a -> 'state * 'a
于 2010-04-07T21:17:04.647 回答
5

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

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

于 2010-04-07T21:13:06.140 回答