加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

Scala延续:序列中有许多变化

发布时间:2020-12-16 18:46:29 所属栏目:安全 来源:网络整理
导读:我一直试图用 scala continuation来解决复杂的输入问题.我一直在阅读我能找到的所有材料,包括延续包中的参考文档.我想我已经在某种程度上弄明白了,当你想到这一点时它会让你觉得有些意义. 我认为我对它的理解(以及我的一些问题)可以通过这个程序得到最好的总
我一直试图用 scala continuation来解决复杂的输入问题.我一直在阅读我能找到的所有材料,包括延续包中的参考文档.我想我已经在某种程度上弄明白了,当你想到这一点时它会让你觉得有些意义.

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

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的理解缺少什么?在@cpsParam [B,C]中是否存在不希望Unit为B的情况?
>上面的程序编译和工作(打印出“5.0”).但是,我现在遇到的问题导致我的困惑是当我更改重置块以尝试在method1之后调用method2时:

(显然你不能在列表后面有一个代码块)

reset {
  val f = methods.method1(5);
  println(f);
  val s = methods.method2(42);
  println(s);
}

当我这样做(这看起来很简单),我在重置时得到以下编译器错误(这是scala 2.10里程碑2):

illegal answer type modification: scala.util.continuations.cpsParam[Unit,Float] andThen scala.util.continuations.cpsParam[Unit,String]

我将此解释为“你的第一个班次返回一个浮点数,你的第二个班次返回一个字符串,你不能这样做.”这准确吗?这是否意味着你不能使用CPS按顺序执行两个(或更多)事情,除非它们具有相同的返回类型?因为这似乎是一种严重的限制.我猜测我要么是1)缺少允许你这样做的东西,或者B)遗漏了一些明显的原因,为什么CPS不可能发生这种情况.但是它是哪一个?

我开始觉得你不需要成为一名博士后学生才能理解斯卡拉的CPS.但我当然还没到那里.

解决方法

在我提出这个问题之后,我做了更多的研究,我想我现在能够回答我自己的问题(我希望这不是一个失礼).

我做了三件事让我理解了这个问题,我认为任何遇到scala延续问题的人都会很好地遵循这些步骤:

>阅读the original academic paper about scala’s continuations.它非常干燥,我怀疑它主要是一群疯子的乱语,但它也非常有用,因为它让你深入了解编译器如何转换你的连续驱动代码和打字和这样做会带来纯度问题.
>以回调传递方式重写代码.这是您可以做的最重要的事情,以真正处理继续流及其类型的流程.
>检查,我的意思是检查班次的类型签名,并注意它正在做什么.这将带你到我所拥有的顿悟.

在我的情况下,我输入@cpsParams和cb参数来改变所有错误.我将解释我是如何弄清楚我做错了什么的,这样任何像我一样愚蠢的人都可以遵循相同的步骤,并希望在继续编译器让他们发疯的时候获得一些见解.

步骤1

我读了上面的论文.大约十几次.我仍然很清楚.但我所理解的是非常有帮助的.

第2步

我在回调传递样式中重写了我的重置块,假装它不是一个移位,而是每个方法都有一个名为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步结论

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

第3步

现在我看着班次的签名.就在我面前:

def shift[A,B,C](fun: (A => B) => C)): A @cpsParam[B,C]

当我看到这个时,我意识到结合我的回调式练习,这里有足够的信息(即使没有完全理解幕后的转变),这基本上是一个维度分析练习.

我知道method1的结果将是一个Float.因此,继续回调(在上面表示为(A => B))需要接受Float作为其参数.这将A修复为Float.因此,method1现在看起来像这样:

def method1(param:Int): Float @cpsParam[B,C] = shift { cb: (Float => B) => {
    ...
    C
  }
}

换句话说,我传递给shift的函数必须从Float到B取一个函数,然后返回C.我从练习中知道回调应该返回Unit或者事情变得混乱.我也知道在我的回调练习中,方法本身应该显然返回Unit,因为它们将实际结果作为参数传递给continuation.这类似于C也是单位.所以这意味着method1必须如下所示:

def method1(param:Int): Float @cpsParam[Unit,Unit] = shift { cb: (Float => Unit) => {
    cb(param);
  }
}

并且method2将是相同的,除了回调将采用String.

我学到的是

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

这意味着,据我所知,B和C在转变为单位以外的任何东西方面都没有太多的目的.这是完全合理的,因为有一个注释@suspendable是@cps [Unit]的快捷方式,它是@cpsParam [Unit,Unit]的快捷方式.

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

美好结局

具有上述变化的程序完全编译并按顺序运行这两种方法.所以这让我相信我终于做对了.如果您是具有深入了解CPS的博士,那么请纠正我的任务中的任何不准确之处.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读