9

Does Scala have an equivelent to golangs defer?

from: http://golang.org/doc/effective_go.html#defer

Go's defer statement schedules a function call (the deferred function) to be run immediately before the function executing the defer returns. It's an unusual but effective way to deal with situations such as resources that must be released regardless of which path a function takes to return. The canonical examples are unlocking a mutex or closing a file.

Sam Hosseini
  • 813
  • 2
  • 9
  • 17
Ryan Leach
  • 4,262
  • 5
  • 34
  • 71

3 Answers3

9

Scala does not offer defer by design, however you can create it yourself by wrapping your function in another function, passing an object which keeps track of functions to call.

Example:

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
}

In this example, Deferable would be the wrapping function which calls context and returns it contents, giving it an object which tracks defer calls.

You can use this construct like this:

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))

The output would be:

before return
dtest: 6
dtest: 3
3

I'm aware that this can be solved differently but it is really just an example that Scala supports the concept of defer without too much fuss.

nemo
  • 55,207
  • 13
  • 135
  • 135
  • 2
    You may also want some sort of exception handling in this - go uses defer() to clean up after errors, but they don't really have exceptions so they don't need to handle them. Scala does, so it may be worth trying to catch exceptions in the final handler. – riri Nov 17 '15 at 00:35
6

I can't think of a Scala specific way, but wouldn't this be equivalent (though not as pretty):

try {
    // Do stuff   
} finally {
    // "defer"
}
fresskoma
  • 25,481
  • 10
  • 85
  • 128
  • Almost, by implementing a stack based resolution, defer can be called on the line immediately after locking a mutex or opening a file, allowing a programmer to see immediately that the mutex or file is being unlocked or closed correctly at the end of the function. – Ryan Leach Sep 17 '13 at 08:55
  • Yeah, I meant "equivalent [in its functionality]" :) I do not think that there is an additional language feature to do this, though. – fresskoma Sep 17 '13 at 08:58
  • @RyanTheLeach, I really don't see that as a plus. – itsbruce Sep 17 '13 at 09:02
  • Regardless of whether it is a plus or not it is one of the defining differences. – Ryan Leach Sep 17 '13 at 09:15
2

No. Go has this construct precisely because it doesn't support exceptions and has no try...finally syntax.

Personally, I think it invites a maintenance nightmare; calls to defer can be buried anywhere in a function. Even where responsible coders put the defers right beside the thing to be cleaned up, I think it's less clear than a finally block and as for what it lets the messy coders do... at least finally blocks put all the clean-up in one place.

defer is the opposite of idiomatic Scala. Scala offers monadic ways to control program flow and defer magic does not contribute at all. Monads offer a functional improvement over try...finally which let you

  • Define your own error handling flow
  • Manipulate defined flows functionally
  • Make a function's anticipated errors part of its signature

defer has no place in this.

itsbruce
  • 4,825
  • 26
  • 35
  • 2
    Um. `defer func() {whatever();was();in();finally();block()}`, I don't see any disadvantage here. You can argue that `RAII` or the equivalent `using` from `C#` and `python` is less messy, but `finally`? It's the exact same beast. – Elazar Leibovich Sep 17 '13 at 09:26
  • 5
    Go has panic/recover, which are close to exceptions. Having the `defer` statement right after the open() (or whatever) call is a big plus for _me_. I don't like to scroll down all the way to the finally block to see if the author has really closed the file (or whatever). – topskip Sep 17 '13 at 09:27
  • 1
    @topskip I much prefer the monadic approach to controlling program flow. See http://stackoverflow.com/questions/10857973/scala-return-has-its-place for an excellent discussion of this. – itsbruce Sep 17 '13 at 09:33
  • @ElazarLeibovich Maybe you made your comment before I finished adding the monadic part of my answer. `finally` blocks are not my preferred way to deal with this. That said, I don't think `defer` is at all an improvement. – itsbruce Sep 17 '13 at 09:35
  • 2
    It's easy enough to mimic C#'s `using` in Scala, [example here](https://gist.github.com/Patryczek/243377a7b482f3c5fa75), so then you can just do `using(resource) { res => res.doSomething(); res.doSomethingElse(); }` or, if it's a simple, one-time operation, then `val result = using(resource) { _.read(); }`, with appropriate identation of course... :) – Patryk Ćwiek Sep 17 '13 at 09:39
  • 1
    @PatrykĆwiek that's almost the opposite approach to `defer`, though. Different semantics and a much clearer structure. If you wanted to add your own answer about `using`, I'd happily vote it up, while commenting about how different it is to `defer` ;) – itsbruce Sep 17 '13 at 09:46