I like the way, you can write one-liner-methods in Scala, e.g. with List(1, 2, 3).foreach(..).map(..)
.
But there is a certain situation, that sometimes comes up when writing Scala code, where things get a bit ugly. Example:
def foo(a: A): Int = {
// do something with 'a' which results in an integer
// e.g. 'val result = a.calculateImportantThings
// clean up object 'a'
// e.g. 'a.cleanUp'
// Return the result of the previous calculation
return result
}
In this situation we have to return a result, but can not return it directly after the calculation is done, because we have to do some clean up before returning.
I always have to write a three-liner. Is there also a possibility to write a one-liner to do this (without changing the class of A
, because this may be a external library which can not be changed) ?
There are clearly side-effects involved here (otherwise the order of invocation of calculateImportantThings
and cleanUp
wouldn't matter) so you would be well advised to reconsider your design.
However, if that's not an option you could try something like,
scala> class A { def cleanUp {} ; def calculateImportantThings = 23 }
defined class A
scala> val a = new A
a: A = A@927eadd
scala> (a.calculateImportantThings, a.cleanUp)._1
res2: Int = 23
The tuple value (a, b)
is equivalent to the application Tuple2(a, b)
and the Scala specification guarantees that its arguments will be evaluated left to right, which is what you want here.
This is a perfect use-case for try
/finally
:
try a.calculateImportantThings finally a.cleanUp
This works because try/catch/finally is an expression in scala, meaning it returns a value, and even better, you get the cleanup whether or not the calculation throws an exception.
Example:
scala> val x = try 42 finally println("complete")
complete
x: Int = 42
There is, in fact, a Haskell operator for just such an occasion:
(<*) :: Applicative f => f a -> f b -> f a
For example:
ghci> getLine <* putStrLn "Thanks for the input!"
asdf
Thanks for the input!
"asdf"
All that remains then is to discover the same operator in scalaz, since scalaz usually replicates everything that Haskell has. You can wrap values in Identity
, since Scala doesn't have IO
to classify effects. The result would look something like this:
import scalaz._
import Scalaz._
def foo(a: A): Int =
(a.calculateImportantThings.pure[Identity] <* a.cleanup.pure[Identity]).value
This is rather obnoxious, though, since we have to explicitly wrap the side-effecting computations in Identity. Well the truth is, scalaz does some magic that implicitly converts to and from the Identity container, so you can just write:
def foo(a: A): Int = Identity(a.calculateImportantThings) <* a.cleanup()
You do need to hint to the compiler somehow that the leftmost thing is in the Identity monad. The above was the shortest way I could think of. Another possibility is to use Identity() *> foo <* bar
, which will invoke the effects of foo
and bar
in that order, and then produce the value of foo
.
To return to the ghci example:
scala> import scalaz._; import Scalaz._
import scalaz._
import Scalaz._
scala> val x : String = Identity(readLine) <* println("Thanks for the input!")
<< input asdf and press enter >>
Thanks for the input!
x: String = asdf
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With