这个可能是一个愚蠢的,但看看(Eliminating my explicit state passing via like, monads and stuff)
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
有什么好处。这只是一种安全预防措施吗?
答案 0 :(得分:3)
我认为这更多的是偏好或方便而非安全;有些人喜欢将函数包装在一个单例区分的联合类型中,有些人不喜欢。
我不喜欢包装函数,因为它引入了少量的额外开销,并且可能会阻止编译器进行一些优化。在我的ExtCore库中,我实现了相同的功能,使用类型别名而不是创建实际类型来包装函数:
type StateFunc<'State, 'T> = 'State -> 'T * 'State
答案 1 :(得分:2)
我的猜测是包装器来自Haskell传统和可以概括为monad的语言。
在那些laguanges中,您可以使用通用的>>=
函数,但每种类型只能有一个实现,有时候会有多个有用的实现。
非常类型的情况就是'a * 'b
和'a->'b
。
对于函数'a -> 'b
,您可以定义一个读取器,一个状态或解析器monad,告诉哪个实现拾取包装它们的方式,因此它们有不同的类型。
在F#中,事情是不同的,大多数monad库没有定义通用>>=
实现,因为在.NET中没有干净的方法来执行此操作,所以没有必要包装State因为你将是明确地应用>>=
和其他monad相关函数的具体实现。
如果没有泛型函数或重载,你仍然可以根据需要包装状态monad,但是你必须像在Haskell中一样包装和解包代码,在这种情况下,决定取决于你想要定制多少你的类型是general question,不仅仅是monad。