Delay如何在continuation monad中正常工作以防止stackoverflow?

时间:2013-07-22 16:04:15

标签: f# workflow stack-overflow monads continuation

这是对此的参考问题:StackOverflow in continuation monad
与我一起玩了一点,需要一些澄清。

1)我想这个:

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

使得计算工作流程中的行为能够实现这一点之间的差异:

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的返回值为:('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"
MAP_FIXED


地图[1; 2] 地图[2] 地图[] cBind [] 地图[] cBind [3] 地图[2] 地图[] cBind [] 地图[] [2; 3]

图:
地图[1; 2] 地图[2] 地图[] cBind [] cBind [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

1 个答案:

答案 0 :(得分:3)

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

关于你的其他问题,我在你的第二个问题中找不到你所问的内容。对于第三个问题,编译器推断出Bind可能的最一般类型。你是对的,你可能期望的传统类型比这更具体,但是你可以在更广泛的上下文中调用Bind并不是一个真正的问题。如果你真的想要一个更具体的类型,你总是可以添加注释来约束签名。