Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F# 无堆栈蹦床Monad/计算表达式_F#_Functional Programming - Fatal编程技术网

F# 无堆栈蹦床Monad/计算表达式

F# 无堆栈蹦床Monad/计算表达式,f#,functional-programming,F#,Functional Programming,我正在研究一种自己设计的函数式编程语言,我偶然发现了一个我无法解决的问题。我想知道是否有人对如何解决这一问题有任何建议,或是不可能的原因 下面的代码概述了一个解决方案,它不是理想的,而是折衷的 这个问题是我当前使用的运行时系统的核心。我不再依赖.Net堆栈,而是使用monad在蹦床上执行操作。这将有助于逐步调试,并允许用户不必担心堆栈空间。这是我目前使用的单子的简化版本 type 't StackFree = |Return of 't

我正在研究一种自己设计的函数式编程语言,我偶然发现了一个我无法解决的问题。我想知道是否有人对如何解决这一问题有任何建议,或是不可能的原因

下面的代码概述了一个解决方案,它不是理想的,而是折衷的

这个问题是我当前使用的运行时系统的核心。我不再依赖.Net堆栈,而是使用monad在蹦床上执行操作。这将有助于逐步调试,并允许用户不必担心堆栈空间。这是我目前使用的单子的简化版本

type 't StackFree =
    |Return of 't                                 //Return a value
    |StackPush of ('t->'t StackFree)*'t StackFree //Pushes a return handler onto the "Stack"
    |Continuation of (unit->'t StackFree)         //Perform a simple opperation
type StackFreeMonad() =
    member this.Delay(fn) = 
        Continuation(fn)
    member this.Bind(expr,fn) =
        StackPush(fn,expr)
    member this.Return(value) =
        Return(value)
    member this.ReturnFrom(x) =x
let stackfree = StackFreeMonad()
这不是最初的设计,但这是我在理想世界中使用F#计算表达式所能达到的最佳效果。上述计算表达式适用于这种类型

type 't Running = 
    |Result of 't
    |Step of (unit->'t Running)
因此,为了将StackFree转换为Running类型,我必须使用这个转换函数

//this method loops through the StackFree structure finding the next computation and managing a pseudo stack with a list.
let prepareStackFree<'t> :'t StackFree->'t Running =
    let rec inner stack stackFree = 
        Step(fun ()-> 
            match stackFree with 
            //takes the return values and passes it to the next function on the "Stack"
            |Return(value)-> 
                match stack with 
                |[]->Result(value)
                |x::xs -> inner xs (x value)
            //pushes a new value on the the "Stack"
            |StackPush(ret,next) ->
                inner (ret::stack) next
            //performs a single step
            |Continuation(fn)->
                inner stack (fn()))
    inner []
上述计算表达式中的绑定实现创建了一个调用另一个函数的函数。因此,随着您越来越深入地调用bind,您必须执行一系列函数调用,然后最终遇到stackoverflow异常


Edit2:清晰

迟做总比不做强

本手册第4节对此进行了说明。Bjarnason通过向
Trampoline
数据类型添加一个新的构造函数来解决这个问题,它表示对另一个Trampoline的子例程调用。他将这个新构造函数保持私有,以确保您不能构建左嵌套的
Bind
s(在执行蹦床时会溢出堆栈)

我决不是一个骗子,但我会蒙混过关的。在WishF#ul(我刚刚编造的一种虚构的F#方言)中,你可以直接表达新的存在量化构造器:

type Tram<'a> =
    | Done of 'a
    | Step of (unit -> Tram<'a>)
    | Call<'x> of Tram<'x> * ('x -> Tram<'a>)  // don't export this

type TramMonad() =
    member this.Return(x) = Done(x)
    member this.Bind(ma, f) = match ma with
        | Call(mx, k) -> Call(mx, fun x -> this.Bind(k(x), f))
        | _ -> Call(ma, f)
    // i confess to not quite understanding what your Delay and ReturnFrom methods are for

let tram = new TramMonad()

let rec runTram t =
    let next mx f = match mx with
        | Done(x) -> f x
        | Step(k) -> Step(fun () -> tram.Bind(k(), f))
        | Call(my, g) -> tram.Bind(my, fun x -> tram.Bind(g x, f))

    match t with
        | Done(x) -> x
        | Step(k) -> runTram(k())
        | Call(mx, f) -> runTram(next mx f)

我推荐阅读,它继续将这种无堆栈结构推广到任何自由单子,而不仅仅是蹦床(它们是
免费的(Unit->)
)。Phil Freeman在这项工作的基础上,将trampoline paper的免费monad概括为免费monad transformer。

如果我运行您的示例代码,它在没有
StackOverflowException的情况下运行良好,因此我猜(在没有真正阅读代码的情况下),它没有使用堆栈(至少不用于递归调用)。这是您看到的,还是不是您期望的?是的,示例代码运行良好,没有堆栈溢出异常。问题是有没有办法摆脱中间层管理“堆栈”列表。我解决了这个问题,我能够将prepareStackFree混合到StackFree monad中。但它仍然需要向其传递一个列表。我仍然想知道是否有可能没有一个列表来表示堆栈。谢谢!迟到总比不迟到好。我没有时间来讨论这个问题,但一旦我做了,我肯定会将它标记为已回答。我引入了另一种类型来静态地强制执行不变量,即绑定在其LHS中永远不会有绑定:
type RunningMonad() = 
    member this.Delay(fn) =
        Step(fun ()->fn ())
    member this.Bind(m, fn) = 
        Step(fun ()->
            match m with
            |Result(value)-> fn value
            //Here is the problem
            |Step(next)-> this.Bind(next(),fn))
    member this.Return(v) =
        Result(v)
    member this.ReturnFrom(x) = x
type Tram<'a> =
    | Done of 'a
    | Step of (unit -> Tram<'a>)
    | Call<'x> of Tram<'x> * ('x -> Tram<'a>)  // don't export this

type TramMonad() =
    member this.Return(x) = Done(x)
    member this.Bind(ma, f) = match ma with
        | Call(mx, k) -> Call(mx, fun x -> this.Bind(k(x), f))
        | _ -> Call(ma, f)
    // i confess to not quite understanding what your Delay and ReturnFrom methods are for

let tram = new TramMonad()

let rec runTram t =
    let next mx f = match mx with
        | Done(x) -> f x
        | Step(k) -> Step(fun () -> tram.Bind(k(), f))
        | Call(my, g) -> tram.Bind(my, fun x -> tram.Bind(g x, f))

    match t with
        | Done(x) -> x
        | Step(k) -> runTram(k())
        | Call(mx, f) -> runTram(next mx f)
module rec Trampoline =
    type Call<'a> =
        abstract member Rebind<'b> : ('a -> Tram<'b>) -> Tram<'b>
        abstract member Next : unit -> Tram<'a>
    type Tram<'a> =
        | Done of 'a
        | Step of (unit -> Tram<'a>)
        | Call of Call<'a>

    type TramMonad() =
        member this.Return(x) = Done(x)
        member this.Bind(ma, f) =
            match ma with
                | Call(aCall) -> aCall.Rebind(f)
                | _ -> call ma f
    let tram = new TramMonad()

    let rec call<'a, 'x>(mx : Tram<'x>) (f : 'x -> Tram<'a>) : Tram<'a> = Call {
        new Call<'a> with
            member this.Rebind<'b>(g : 'a -> Tram<'b>) : Tram<'b> =
                call<'b, 'x> mx (fun x -> tram.Bind(f x, g) : Tram<'b>)
            member this.Next() =
                match mx with
                    | Done(x) -> f x
                    | Step(k) -> Step(fun () -> tram.Bind(k(), f))
                    | Call(aCall) -> aCall.Rebind(f)
    }

    let rec runTram t =
        match t with
            | Done(x) -> x
            | Step(k) -> runTram(k())
            | Call(aCall) -> runTram(aCall.Next())