1

这可能是一个愚蠢的,但是,看看(消除我通过like、monads和其他东西传递的显式状态

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

type StateBuilder<'s>() =
  member x.Return v : State<'s,_> = State(fun s -> v,s)
  member x.Bind(State v, f) : State<'s,_> =
    State(fun s ->
      let (a,s) = v s
      let (State v') = f a
      v' s)

let withState<'s> = StateBuilder<'s>()

let getState = State(fun s -> s,s)
let putState v = State(fun _ -> (),v)

let runState (State f) init = f init

包裹起来有什么's -> 'a * 's好处State。仅仅是为了安全吗?

4

2 回答 2

3

我认为这更多的是偏好或便利而不是安全。有些人喜欢将函数包装在这样的单例区分联合类型中,而有些人则不喜欢。

我不喜欢包装函数,因为它会引入少量额外开销,并且会阻止编译器进行一些优化。在我的ExtCore库中,我实现了同样的事情,使用类型别名而不是创建实际类型来包装函数:

type StateFunc<'State, 'T> = 'State -> 'T * 'State
于 2013-11-24T22:43:35.583 回答
2

我的猜测是包装器来自 Haskell 传统和可以泛化单子的语言。在这些语言中,您可以拥有一个通用>>=函数,但每种类型只能有一个实现,有时会有不止一个有用的实现。

这是非常通用的类型(例如'a * 'b和)的情况'a->'b

对于一个函数'a -> 'b,您可以定义一个读取器、一个状态或一个解析器 monad,判断哪个实现拾取的方法是将它们包装起来,因此它们具有不同的类型。

在 F# 中情况不同,大多数 monad 库没有定义通用>>=实现,因为在 .NET 中没有干净的方法来执行此操作,因此无需包装状态,因为您将明确应用特定的实现>>=和其他monad 相关函数。

在没有通用函数或重载的情况下,您仍然可以根据需要包装您的 State monad,但是您必须像在 Haskell 中一样包装和解包代码,在这种情况下,决定取决于您要自定义多少类型和这是一个普遍的问题,不仅仅是单子。

于 2013-11-25T06:19:33.650 回答