StackOverflow在continuation monad中

时间:2012-06-25 11:47:13

标签: f# monads computation-expression

使用以下continuation monad:

type ContinuationMonad() =
    member this.Bind (m, f) = fun c -> m (fun a -> f a c)
    member this.Return x = fun k -> k x

let cont = ContinuationMonad()

我不明白为什么下面给我一个堆栈溢出:

let map f xs =
    let rec map xs =
        cont {
            match xs with
            | [] -> return []
            | x :: xs ->
                let! xs = map xs
                return f x :: xs
        }
    map xs id;;

let q = [1..100000] |> map ((+) 1)

以下情况不是:

let map f xs =
    let rec map xs =
        cont {
            match xs with
            | [] -> return []
            | x :: xs ->
                let! v = fun g -> g(f x)
                let! xs = map xs
                return v :: xs
        }
    map xs id;;

let q = [1..100000] |> map ((+) 1)

2 个答案:

答案 0 :(得分:7)

要修复您的示例,请将此方法添加到monad的定义中:

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

显然,溢出的部分是map的递归调用中的大输入列表的破坏。延迟它解决了这个问题。

请注意,您的第二个版本将map的递归调用置于另一个let!后面,Bindmap以及一个额外的lambda,实际上会延迟对StackOverflow的递归调用

在得出这个结论之前,我不得不追求一些虚假的道路。除非递归调用被延迟,否则观察到OCaml N也会被F#抛出(虽然更高OCaml)。虽然let cReturn x = fun k -> k x let cBind m f = fun c -> m (fun a -> f a c) let map f xs = (* inner map loop overflows trying to pattern-match long lists *) let rec map xs = match xs with | [] -> cReturn [] | x :: xs -> cBind (map xs) (fun xs -> cReturn (f x :: xs)) in map xs (fun x -> x) let map_fixed f xs = (* works without overflowing by delaying the recursive call *) let rec map xs = match xs with | [] -> cReturn [] | x :: xs -> cBind (fun c -> map xs c) (fun xs -> cReturn (f x :: xs)) in map xs (fun x -> x) let map_fused f xs = (* manually fused version avoids the problem by tail-calling `map` *) let rec map xs k = match xs with | [] -> k [] | x :: xs -> map xs (fun xs -> k (f x :: xs)) in map xs (fun x -> x) TCO有一些怪癖,但{{1}}更加成熟,所以这让我确信问题确实存在于代码中,而不是编译器:

{{1}}

答案 1 :(得分:4)

F#编译器有时不是很聪明 - 在第一种情况下它会计算map xs然后计算f x然后加入它们,因此map xs不在尾部位置。在第二种情况下,它可以轻松地将map xs重新排序为尾部位置。