【问题标题】:How does the Delay exactly works in continuation monad to prevent stackoverflow?Delay 如何在 continuation monad 中准确地工作以防止 stackoverflow?
【发布时间】:2013-07-21 11:08:18
【问题描述】:

这是一个参考问题:StackOverflow in continuation monad
我和谁玩了一点,需要澄清一下。

1) 我想是这样的:

member this.Delay(mk) = fun c -> mk () c

使计算工作流中的行为在这些之间做出 toyvo 所示的差异:

cBind (map xs) (fun xs -> cReturn (f x :: xs))  

cBind (fun c -> map xs c) (fun xs -> cReturn (f x :: xs))

所以我不完全明白什么是诀窍,当
(fun c -> map xs c) 只是 (map xs) 的不同表示法

2) 推理问题。 - 在 OP 的第二个地图示例中,我发现由于v 值的推断问题,它无法编译,因为它将f 推断为a -> b list,而不是所需的a -> b。为什么它以这种方式推断?如果let v = f x 会很好地推断出来。

3) 在我看来,VS 在工具提示中显示了不准确的类型签名: monad的Return的返回类型是:('e->'f)->f,而Bind的返回类型只有'c->'b。 - 在 Bind 情况下,它似乎将('e->'f) 简化为仅c,还是我在这里遗漏了什么?

感谢您的澄清,
托马斯

编辑 - 测试转储:

let cReturn x = fun k -> k x
let cBind m f = 
    printfn "cBind %A" <| m id
    fun c -> m (fun a -> f a c)

let map_fixed f xs =
  let rec map xs =
    printfn "map %A" xs
    match xs with
      | [] -> cReturn []
      | x :: xs -> cBind (fun c -> map xs c) (fun xs -> cReturn (f x :: xs)) 
  map xs (fun x -> x)

let map f xs =
  let rec map xs =
    printfn "map %A" xs
    match xs with
      | [] -> cReturn []
      | x :: xs -> cBind (map xs) (fun xs -> cReturn (f x :: xs)) 
  map xs (fun x -> x)

[1..2] |> map_fixed ((+) 1) |> printfn "%A"
[1..2] |> map ((+) 1) |> printfn "%A"

地图固定:
地图 [1; 2] 地图 [2] 地图 [] c绑定 [] 地图 [] c绑定 [3] 地图 [2] 地图 [] c绑定 [] 地图 [] [2; 3]

地图:
地图 [1; 2] 地图 [2] 地图 [] c绑定 [] c绑定 [3] [2; 3]

修改问题 2:

let map f xs =
    let rec map xs =
        cont {
            match xs with
            | [] -> return []
            | x :: xs ->
                let v = f x // Inference ok
                //let! v = cont { return f x } // ! Inference issue - question 2
                let! xs = map xs
                return v :: xs
        }
    map xs id

【问题讨论】:

  • 关于你的第二个问题,推理似乎对我来说很好。
  • 如果我取消注释 //let!.. 它会给我:类型不匹配。期待一个 'a 但给出了一个 'a 列表
  • 我终于看到了错误 - 在我的绑定定义中 - 与第 1 点类似的日志 - 愚蠢的我,感谢您的关注。

标签: f# workflow stack-overflow monads continuation


【解决方案1】:

问题正是fun c -&gt; map xs c map xs 相同。它们在某种意义上具有相同的“含义”,但它们的运行时语义不同。在后一种情况下,计算表达式会导致立即调用 map 函数,并将 xs 作为参数(返回另一个函数作为结果)。另一方面,评估fun c -&gt; map xs c 不会导致立即调用map!对map 的调用会延迟到实际应用结果函数。这是防止堆栈溢出的关键区别。

关于你的其他问题,我不太清楚你在第二个问题中问的是什么。对于您的第三个问题,编译器已推断出Bind 的最通用类型。没错,您可能期望的传统类型比这更具体,但是您可以在比严格必要的更广泛的上下文中调用Bind 并不是一个真正的问题。如果你真的想要一个更具体的类型,你可以随时添加注释来约束签名。

【讨论】:

  • 关于我的第二个问题,当您尝试编译第二个操作的函数时,您会看到问题所在 - 函数参数 f 的类型对我来说是出乎意料的。
  • -“不会导致立即调用 map”-但根据我的测试转储它会-在评估 cBind 之前-查看我的编辑...
  • @tomasK - 您的测试存在缺陷,因为您在 cbind 的日志记录中应用了 m。将该行更改为printfn "cbind",您会看到map_fixed 确实延迟调用map
  • 真的!我没有意识到这一点。谢谢!
猜你喜欢
  • 2011-09-29
  • 2015-12-23
  • 2015-03-01
  • 2020-03-26
  • 2014-09-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多