2

如果这个问题的答案是“你做错了”,一定要让我知道一个更合适的方法。我的代码结构如下:

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

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

State 是通过 DC 进行的,但是这样做很麻烦,所以我将其更改为以下内容,这是我在与 state monad 讨论中看到的常见签名。

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

然后我决定把它提升到一个新的水平并引入延迟单子(或最终或任何它的名字)。所以我改变了我的类型,使其递归:

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

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

当统一 ''a' 和 'Res<'a> -> Res<'b>' 时,结果类型将是无限的

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

或者我似乎无法正确输入类型,以下(可以理解)引发类型错误,但我有一个盲点来修复它:

此表达式应具有类型“Res<'a>”,但此处具有类型“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延续应该已经返回这种类型。

另外,我目前的回报如下(跟随史蒂夫霍斯菲尔德):

let result v = Res.V v

但在一些帖子(特别是热闹的 Frankenfunctor )中也注意到了这一点:

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

也许我遵循了我不应该遵循的模式,但当时这似乎是个好主意,尤其是在尝试重构大量样板代码并用计算表达式替换它时,但也许我应该坚持直接评估,它适合在我脑海中 ;)。

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

4

1 回答 1

1

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

您的错误是包装的函数Res.E应该返回一个元组,但您只是Res<'b>通过递归调用来返回它bind。为了反映这一点,您将结果传递e给递归bind调用来代替Res<'b>参数,而e实际上返回一个元组。

要正确执行此操作,您需要解构 的结果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情况的解释应该是“一种不改变状态并返回的计算x”,在这种情况下,延迟它只会增加一个额外的 lambda 表达式。

于 2015-11-02T20:25:04.243 回答