gpt4 book ai didi

f# - Stackless trampoline Monad/计算表达式

转载 作者:行者123 更新时间:2023-12-03 23:54:03 39 4
gpt4 key购买 nike

我正在开发一种我自己设计的函数式编程语言,我偶然发现了一个超出我解决能力的问题。我想知道是否有人对如何解决它有任何建议,或者为什么它是不可能的。

下面的代码概述了一个不是理想但妥协的解决方案。

这个问题是我目前使用的运行时系统的核心。我没有依赖 .Net 堆栈,而是使用 monad 在蹦床上执行操作。这应该有助于逐步调试,并使用户不必担心堆栈空间。这是我目前使用的 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 []

以下是这两种类型的简要示例。
let run<'t> :'t StackFree->'t = 
let rec inner = function
|Step(x)-> inner (x())
|Result(x)-> x
stackFreeToRunning>>inner
//silly function to recompute an intiger value using recursion
let rec recompute number = stackfree {
if number = 0 then return 0
else
let! next = recompute (number-1)
return next+1
}
let stackFreeValue = recompute 100000
let result = run stackFreeValue
do printfn "%i" result

我花了几个小时试图获得一个直接在 Running 类型上工作的计算表达式,并去掉中间人 StackFree。但是我不知道该怎么做。在这一点上,我正在认真考虑不可能解决这个问题的可能性。但是我无法弄清楚这是不可能的原因。

我已经接近了几次,但最终的解决方案最终以某种令人困惑的方式使用了堆栈。

是否可以在不使用 .Net 堆栈的情况下对 Running 类型进行运算的计算表达式?如果这不可能,为什么不可能。我一定缺少一些简单的数学推理。

注意:这些不是我使用的实际类型,它们针对这个问题进行了简化,真正的类型会跟踪脚本中的范围和位置。此外,我知道这种抽象的严重性能成本

编辑:这是解决问题的另一种方法。这个实现是有缺陷的,因为它使用了堆栈。有没有在不使用堆栈的情况下获得下面的确切行为?
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

上述计算表达式中的绑定(bind)实现创建了一个调用另一个函数的函数。因此,随着您越来越深入并越来越多地调用 bind,您必须追逐一堆函数调用,然后最终遇到 stackoverflow 异常。

编辑2:明晰。

最佳答案

迟到总比不到好!

这在 Stackless Scala with Free Monads 的第 4 节中得到解决。 . Bjarnason 通过向 Trampoline 添加一个新的构造函数来解决这个问题。数据类型,表示对另一个蹦床的子例程调用。他将这个新的构造函数保持为私有(private),以确保您不能构建左嵌套 Bind s(执行蹦床时会溢出堆栈)。

我绝不是 F#er,但我会应付自如。在我刚刚编造的一个虚构的 F# 方言 WishF#ul 中,您可以直接表达新的存在量化构造函数:

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)

请注意,所有对 runTram 的递归调用处于尾部位置。这有点令人费解,但你可以说服自己 Bind不会构建深度嵌套的延续,所以 runT将始终在 O(1) 堆栈空间中运行。

遗憾的是,我们使用的是 F#,而不是 WishF#ul,因此我们不得不求助于 Call 中存在类型的面向对象编码。构造函数。开始...
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())

我推荐阅读 the whole paper ,它继续将这种无堆栈结构推广到任何自由单子(monad),而不仅仅是蹦床(它们是 Free (Unit -> _))。菲尔弗里曼的 Stack Safety for Free基于这项工作,将蹦床纸的免费单子(monad)推广到免费单子(monad)变压器。

关于f# - Stackless trampoline Monad/计算表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45152039/

39 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com