【问题标题】:Does Scala have an equivalent to golangs defer?Scala 是否有相当于 golangs defer 的功能?
【发布时间】:2013-09-17 08:45:46
【问题描述】:

Scala 是否具有与 golangs defer 等效的功能?

来自: http://golang.org/doc/effective_go.html#defer

Go 的 defer 语句安排函数调用(延迟函数)在执行 defer 的函数返回之前立即运行。这是一种不寻常但有效的方式来处理诸如无论函数采用哪条路径返回都必须释放资源的情况。典型示例是解锁互斥锁或关闭文件。

【问题讨论】:

    标签: scala go


    【解决方案1】:

    Scala 没有通过设计提供defer,但是您可以通过包装自己创建它 您的函数在另一个函数中,传递一个跟踪要调用的函数的对象。

    例子:

    class DeferTracker() {
      class LazyVal[A](val value:() => A)
    
      private var l = List[LazyVal[Any]]()
      def apply(f: => Any) = { l = new LazyVal(() => f) :: l }
      def makeCalls() = l.foreach { x => x.value() }
    }
    
    def Deferrable[A](context: DeferTracker => A) = {
      val dt = new DeferTracker()
      val res = context(dt)
      dt.makeCalls
      res
    }
    

    在本例中,Deferable 将是调用 context 和 返回它的内容,给它一个跟踪defer调用的对象。

    你可以像这样使用这个结构:

    def dtest(x:Int) = println("dtest: " + x)
    
    def someFunction(x:Int):Int = Deferrable { defer =>
      defer(dtest(x))
      println("before return")
      defer(dtest(2*x))
    
      x * 3
    }
    
    println(someFunction(3))
    

    输出将是:

    before return
    dtest: 6
    dtest: 3
    3
    

    我知道这可以通过不同的方式解决,但这只是一个例子 Scala 支持 defer 的概念,不用大惊小怪。

    【讨论】:

    • 您可能还需要在此进行某种异常处理 - go 使用 defer() 来在错误后进行清理,但它们并没有真正的异常,因此它们不需要处理它们。 Scala 确实如此,因此可能值得尝试在最终处理程序中捕获异常。
    【解决方案2】:

    我想不出 Scala 特定的方式,但这不就是等效的吗(虽然不那么漂亮):

    try {
        // Do stuff   
    } finally {
        // "defer"
    }
    

    【讨论】:

    • 几乎,通过实现基于堆栈的解析,可以在锁定互斥体或打开文件后立即在线调用defer,让程序员立即看到互斥体或文件正在解锁或关闭正确地在函数的末尾。
    • 是的,我的意思是“[在其功能上] 等效”:) 不过,我认为没有额外的语言功能可以做到这一点。
    • @RyanTheLeach,我真的不认为这是一个优点。
    • 无论是不是优点,它都是决定性的区别之一。
    【解决方案3】:

    没有。 Go 有这个结构正是因为它不支持异常并且没有try...finally 语法。

    就我个人而言,我认为这会引发一场维护噩梦;对 defer 的调用可以隐藏在函数的任何地方。即使负责任的编码人员将延迟放在要清理的东西旁边,我认为它也没有finally 块那么清楚,至于它让凌乱的编码器做什么......至少finally 块把所有的干净-在一个地方。

    defer 与惯用的 Scala 相反。 Scala 提供了控制程序流的单子方法,而defer 魔法根本没有贡献。 Monad 提供了对 try...finally 的功能改进,让您

    • 定义您自己的错误处理流程
    • 功能性地操作定义的流
    • 将函数的预期错误作为其签名的一部分

    defer 在此没有位置。

    【讨论】:

    • 嗯。 defer func() {whatever();was();in();finally();block()},我看不出有什么缺点。您可以争辩说RAII 或来自C#python 的等效using 不那么混乱,但是finally?这是完全相同的野兽。
    • Go 有恐慌/恢复,这接近于异常。在 open()(或其他)调用之后有 defer 语句对 me 来说是一大优势。我不喜欢一直向下滚动到 finally 块来查看作者是否真的关闭了文件(或其他)。
    • @topskip 我更喜欢单子方法来控制程序流。请参阅 stackoverflow.com/questions/10857973/scala-return-has-its-place 以获得对此的精彩讨论。
    • 在 Scala 中模仿 C# 的 using example here 很容易,所以你可以只做 using(resource) { res => res.doSomething(); res.doSomethingElse(); } 或者,如果它是一个简单的一次性操作,那么 val result = using(resource) { _.read(); },当然有适当的身份...... :)
    • @PatrykĆwiek 这与defer 几乎相反。不同的语义和更清晰的结构。如果你想添加你自己关于using 的答案,我很乐意投赞成票,同时评论它与defer 有多么不同;)
    猜你喜欢
    • 2012-06-18
    • 2023-03-31
    • 1970-01-01
    • 2014-04-02
    • 2019-10-31
    • 2020-03-22
    • 2012-08-02
    • 2021-07-05
    • 2020-11-23
    相关资源
    最近更新 更多