gpt4 book ai didi

Scala 延续 : many shifts in sequence

转载 作者:行者123 更新时间:2023-12-04 15:25:44 27 4
gpt4 key购买 nike

我一直在努力解决 scala 延续的复杂打字问题。我一直在阅读我能找到的所有 Material ,包括关于 continuations 包的引用文档。我想我已经在某种程度上弄清楚了,当你考虑它时它是有道理的。

我认为我对它的理解(以及我的一些问题)可以通过这个程序得到最好的总结:

package com.whatever;
import scala.util.continuations._;

object methods {

/* The method takes an Int as its parameter. Theoretically, at some point in the future,
* it will return a Float to the remainder of the continuation. This example does it
* immediately but doesn't have to (for example it could be calling a network service
* to do the transformation)
*
* Float @cpsParam[Unit,Float] means that whatever part of the reset{} that is captured
* as a closure should receive a Float and needn't return anything (would it be meaningful
* if Unit were something else?)
*
* The reason I have to return 0.toFloat is so the compiler can properly type the
* method. That zero will never go anywhere. Is this a sign I'm doing it wrong?
*/
def method1(param:Int): Float @cpsParam[Unit,Float] = shift { cb:(Float=>Unit) =>
cb(param.toFloat);
0.toFloat;
}

/* This method is basically identical but returns a String instead of a Float (Again,
* theoretically this would be done by a network service and cb would be called at some
* point in the future.
*/
def method2(param:Int): String @cpsParam[Unit,String] = shift { cb:(String=>Unit) =>
cb(param.toString);
""
}
}

object Main {
def main(args:Array[String]):Unit = {
reset {
val f = methods.method1(5);
println(f);
}
}
}

顺便说一句,StackOverflow 没有突出显示 scala 是犯罪行为! (我的立场更正;它实际上做得很好,但不是在实时预览中)

我的问题如下:
  • 从上面程序中的评论来看,我对scala的CPS的理解缺少什么?是否有过您不想要的情况 UnitB@cpsParam[B,C] ?
  • 上面的程序编译并运行(它打印出 "5.0" )。但是,我现在遇到的导致我困惑的问题是当我更改 reset 时阻止尝试拨打 method2之后 method1 :

  • (显然,您不能在列表之后立即添加代码块)
    reset {
    val f = methods.method1(5);
    println(f);
    val s = methods.method2(42);
    println(s);
    }

    当我这样做时(这似乎是一件非常简单的事情),我在重置时收到以下编译器错误(这是 Scala 2.10 Milestone 2):
    illegal answer type modification: scala.util.continuations.cpsParam[Unit,Float] andThen scala.util.continuations.cpsParam[Unit,String]

    我将其解释为“您的第一个类次返回一个 Float,而您的第二个类次返回一个字符串,而您不能这样做。”这是准确的吗?这是否意味着除非它们具有相同的返回类型,否则您不能使用 CPS 依次执行两个(或多个)操作?因为这似乎是一个严重的限制。我猜我要么 1) 缺少允许您这样做的东西,要么 B) 缺少一些明显的原因,为什么 CPS 不可能发生这种情况。但它是哪一个?

    我开始觉得你不需要成为博士后才能理解 Scala 的 CPS。但我肯定还没有到那个程度。

    最佳答案

    在我问了这个问题之后,我做了更多的研究,我想我现在可以回答我自己的问题了(我希望这不是失礼)。

    我做了三件事帮助我理解了这个问题,我认为任何在 Scala 的延续方面遇到问题的人都可以很好地遵循以下步骤:

  • 阅读 the original academic paper about scala's continuations .它非常枯燥,我怀疑它主要是一群疯子的胡言乱语,但它也非常有帮助,因为它让您深入了解编译器如何转换您的延续驱动代码以及它的类型和纯度问题这样做的脸。
  • 以回调传递风格重写您的代码。 这是您可以做的最重要的事情,以真正掌握延续流及其类型的情况。
  • 检查,我的意思是真正检查 shift 的类型签名并注意它在做什么。这将把你带到我的顿悟。

  • 就我而言,我正在输入 @cpsParam s 和 cb shift 的参数都错了。我将解释我如何找出我做错了什么,以便其他像我一样愚蠢的人可以遵循相同的步骤,并希望在延续编译器让他们发疯时获得一些见解。

    第1步

    我阅读了上面的论文。大约十几次。我对它的了解仍然很少。但我所理解的非常有帮助。

    第2步

    我重写了我的 reset以回调传递风格阻止,假装它不是 shift ,每个方法都有一个名为 cb 的第二个参数这将需要一个函数来完成块的其余部分。这是重置块之后的样子:
      methods.method1(5, {f: Int => {
    println(f);
    methods.method2(42, {s: String => {
    println(s);
    });
    });

    看看发生了什么?因此,现在我不再编写看似阻塞的代码,而是自己明确界定延续,并将它们作为函数传递给每个方法。因此,对于这些情况中的每一种,很明显我的每个匿名回调函数都不需要返回任何内容,事实上,它们都应该返回 Unit否则它们会污染它们被传入的方法的返回类型。我认为这就是编译器试图告诉我的(尽管我可能是错的)。

    这是什么 method1必须看起来像我的回调式程序
       def method1(param:Int, cb:(Float=>Unit)):Unit = {
    cb(param.toFloat);
    }
    method2是相似的,但需要一个 (String=>Unit) .现在很明显我的方法也应该返回 Unit否则它们可能会污染回调函数的返回类型。

    步骤 2 结论

    我认为我的很多困惑源于这样一个事实:出于某种原因,我脑海中的画面是每个 shift只捕获到下一个 shift作为延续。当然,事实并非如此。每个 shift必须捕获 reset 的全部其余部分块包括以下所有 shift s 以便它形成一个大的嵌套回调中的回调情况。此外,所有回调和所有 CPS 调用的方法应该总是(据我所知)返回 Unit ,因为它们的结果不仅不会做任何事情,而且可能会污染调用它们的函数的返回类型,等等。

    第 3 步

    现在我看了 shift的签名.它就在我面前:
    def shift[A,B,C](fun: (A => B) => C)): A @cpsParam[B,C]

    当我看到这个时,我意识到结合我的回调式练习,这里有足够的信息给我(即使我没有完全理解 shift 在幕后做了什么)基本上可以把它变成一个维度分析的练习。

    我知道 method1 的结果将是 Float .因此,继续回调(在上面表示为 (A => B))需要接受 Float作为其参数。这修复了 AFloat .因此 method1现在看起来像这样:
    def method1(param:Int): Float @cpsParam[B,C] = shift { cb: (Float => B) => {
    ...
    C
    }
    }

    换句话说,我传递给 shift 的函数必须将一个函数从 Float 带到 B,然后返回 C。好吧,我从我的练习中知道回调应该返回 Unit 否则事情会变得困惑。我也知道在我的回调练习中,方法本身显然应该返回 Unit因为他们将实际结果作为参数传递给延续。这类似于 C 也是单位。所以这意味着 method1必须是这样的:
    def method1(param:Int): Float @cpsParam[Unit,Unit] = shift { cb: (Float => Unit) => {
    cb(param);
    }
    }

    method2除了回调将采用字符串之外,将是相同的。

    我学到的是

    现在在我看来,与其被抛出的所有类型参数弄糊涂,你可以简单地记住,如果你正在编写一个回调驱动的程序,几乎所有涉及的函数都会返回 Unit因为任何结果都作为参数传递而不是返回。

    这意味着,据我所知, B 没有太大的意义。和 Cshift不是 Unit .这是完全合理的,因为有一个注释 @suspendable这是 @cps[Unit] 的快捷方式这是 @cpsParam[Unit,Unit] 的快捷方式.

    我不知道为什么 scala-lang.org 上的例子如此垃圾。但实际上他们需要说的是,“如果你需要使用除 MyReturnType @suspendable 以外的任何东西,那么你可能做错了,顺便说一句, shift 所采用的函数参数也应该返回 Unit 。”那样的话,我还有生命中最后几天宝贵的时光。

    美好结局

    具有我上面提到的更改的程序完全编译并按顺序使用这两种方法运行。所以这让我相信我终于做对了。如果你是一个对 CPS 有深刻理解的博士,那么请纠正我的散文中的任何不准确之处。

    关于Scala 延续 : many shifts in sequence,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9487051/

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