试图让 State 和 Delayed monad 的结合工作

Trying to get bind working for combination of State and Delayed monad

如果这个问题的答案是"you are going about it all wrong",一定要告诉我一个更合适的方法。我的代码结构如下:

type Res<'T> =
    | E of (DC -> 'T)
    | V of 'T

现在这个类型主要是直接使用的,有很多内联代码手动绑定每个案例,这是我试图摆脱的很多样板,所以我想我把它变成一个计算表达。 mapbindapply 并不难。

状态是通过 DC 进行的,但这很难正确,所以我将其更改为以下内容,这是我在与状态 monad 的讨论中看到的常见签名。

type Res<'T> =
    | E of (DC -> DC * 'T)
    | V of 'T

然后我决定将它提升到一个新的水平并引入 Delayed monad(或 Eventually 或任何它的名字)。所以我按如下方式更改了我的类型,使其递归:

type Res<'T> =
    | E of (DC -> DC * Res<'T>)
    | V of 'T

我尝试了以下的一些变体,但不断得到:

The resulting type would be infinite when unifying ''a' and 'Res<'a> -> Res<'b>'

(通常在我不调用 return 时发生,即下面的 Res.E <|

或者我似乎无法正确设置我的类型,以下(可以理解)会引发类型错误,但我在修复它时遇到了盲点:

This expression was expected to have type 'Res<'a>' but here has type 'DC * Res<'a>'.

let rec bind k res =
    match res with
    | Res.V v -> k v      // not delayed, should it be?
    | Res.E e -> 
        Res.E <| fun dc -> bind k (e dc)   // here's my error, on 'e dc'
         //(fun dc -> dc, bind f (e dc))
bind k res

我理解签名应该是('a -> Res<'b>) -> Res<'a> -> XRes<'b>。问题来自正确的递归,至少在我的脑海中。我什至不确定为什么要将结果提供给 Res.E,据我所知,k continuation 应该已经 return 这种类型了。

此外,我目前有 return 如下(在 Steve Horsfield 之后):

let result v = Res.V v

但在一些帖子中也注意到了(特别是 hilarious Frankenfunctor):

let result v = Res.E <| fun dc -> dc, Res.V v

也许我正在遵循一种我不应该遵循的模式,但当时这似乎是个好主意,尤其是在尝试重构无数样板代码并将其替换为计算表达式时,但也许我应该坚持直接评价,很符合我的想法 ;).

但我觉得我很接近...有什么想法吗?

我不太确定这个 monad 的含义是什么,但是
好的,在稍微考虑一下并阅读问题的 header :- ) 之后,我 do 理解它的意思,并且我可以看到如何构建 bind为此。

你的错误是包裹在 Res.E 中的函数应该 return 一个元组,但是你 return 只是 Res<'b> 通过递归调用 [=11] =].为了反映这一点,您将 e 的结果传递给递归 bind 调用以代替 Res<'b> 参数,而 e 实际上 return 是一个元组。

要正确执行此操作,您需要解构 e 的结果,然后将第二部分传递给递归 bind 调用,并将其结果与第一部分配对:

let rec bind (k: 'a -> 'b Res) (res: 'a Res) : 'b Res =
  match res with
  | V a -> k a
  | E e -> 
      E <| fun dc -> 
        let dc', res' = e dc 
        dc', bind k res'

终结案,我想应该不会也拖延吧。也就是说,我没有看到它的原因。据我了解,V x 案例的解释应该是“a computation that doesn't change state and returns x” ,在这种情况下,使其延迟不会增加任何内容,只会增加额外的 lambda-expression.