State Monad,为什么不是一个元组?

thr*_*thr 9 .net monads f# functional-programming

我只是把头包裹在monads周围(至少我想我有),更具体地说是状态monad,有些人比我想象的更聪明,所以我可能会回答这个问题.

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

type State<'a, 'state> = State of ('state -> 'a * 'state)
Run Code Online (Sandbox Code Playgroud)

现在我的问题是:你有什么理由不能在这里使用元组吗?除此之外可能存在歧义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
Run Code Online (Sandbox Code Playgroud)

Tom*_*cek 12

国家单子基本上与类型工作'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
Run Code Online (Sandbox Code Playgroud)

第一种类型的签名更清楚地表明我们在一些monad中编写函数.第二个例子只是一个带两个int值的函数.我认为第一个的主要好处是你可以更容易地意识到这种类型可以用于其他monadic计算(使用书面编写state { ... }).

但是,正如我已经指出的那样,这不是技术要求.人们可能会使用这种风格,因为许多monad来自Haskell,其中monad与类型(例如State<'state, 'res>)相关联而不是计算构建器(例如state),因此为每个monad定义一个新类型听起来是个好主意.哈斯克尔.


Tom*_*cek 6

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

'state -> 'res * 'state
Run Code Online (Sandbox Code Playgroud)

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

// how would we get a value of type 'state in the implementation?
val return : 'a -> 'state * 'a
Run Code Online (Sandbox Code Playgroud)


Bri*_*ian 5

啊,是的,如果问题是:我应该使用带有T型数据值的单标签Discriminated Union,或者我应该只使用T,那么你可以使用任何一种.

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