How to write this three-line insert as single-line?

I like the way you can write single-line methods in Scala, for example. with List(1, 2, 3).foreach(..).map(..) .

But there is a certain situation that sometimes arises when writing Scala code where everything gets a little ugly. Example:

 def foo(a: A): Int = { // do something with 'a' which results in an integer // eg 'val result = a.calculateImportantThings // clean up object 'a' // eg 'a.cleanUp' // Return the result of the previous calculation return result } 

In this situation, we must return the result, but cannot return it immediately after performing the calculation, because we must perform some cleaning before returning.

I always have to write three-line. Is it possible to write a single-line file for this (without changing the class A , since it can be an external library that cannot be changed)?

+7
source share
5 answers

There are obviously side effects (otherwise, the order of treatment of calculateImportantThings and cleanUp does not matter), so you will be advised to review your design.

However, if this is not an option, you can 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 value of tuple (a, b) equivalent to the application Tuple2(a, b) , and the Scala specification ensures that its arguments will be evaluated from left to right, which you need here.

+9
source

This is an ideal use case for try / finally :

 try a.calculateImportantThings finally a.cleanUp 

This works because try / catch / finally is an expression in scala, that is, it returns a value, and even better, you get a cleanup regardless of whether the calculation throws an exception.

Example:

 scala> val x = try 42 finally println("complete") complete x: Int = 42 
+7
source

Maybe you want to use a kestrel combinator? It is defined as follows:

Kxy = x

So, you call it with the value you want to return, and with some side operation that you want to perform.

You can implement it as follows:

 def kestrel[A](x: A)(f: A => Unit): A = { f(x); x } 

... and use it as follows:

 kestrel(result)(result => a.cleanUp) 

More information can be found here: debasish gosh blog .

[UPDATE] As Yaroslav correctly points out, this is not the best use of a pigeon combinator. But there should be no problem to define a similar combinator using a function with no arguments, so instead:

 f: A => Unit 

someone can use:

 f: () => Unit 
+5
source

In fact, the Haskell operator exists only for this case:

 (<*) :: Applicative f => fa -> fb -> fa 

For example:

 ghci> getLine <* putStrLn "Thanks for the input!" asdf Thanks for the input! "asdf" 

All that remains then is to find the same statement in scalaz , since scalaz usually replicates everything that Haskell has. You can transfer values ​​to Identity , since Scala does not have an IO to classify effects. The result will 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 unpleasant, since we must explicitly transfer side calculations to Identity. Well, true, scalaz does some magic that implicitly converts to and from the Identity container, so you can simply write:

 def foo(a: A): Int = Identity(a.calculateImportantThings) <* a.cleanup() 

You need to somehow hint at the compiler, which is the leftmost in the Identity monad. The above was the shortest way I could think of. Another possibility is to use Identity() *> foo <* bar , which will trigger the effects of foo and bar in that order, and then produce the value 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 
+5
source
 class Test { def cleanUp() {} def getResult = 1 } def autoCleanup[A <: Test, T](a: A)(x: => T) = { try { x } finally { a.cleanUp } } def foo[A <: Test](a:A): Int = autoCleanup(a) { a.getResult } foo(new Test) 

You can look at the scala-arm project for class type solutions.

+2
source

All Articles