Clojure offers a macro called doto
that takes its argument and a list of functions and essentially calls each function, prepending the (evaluated) argument:
(doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2))
-> {a=1, b=2}
Is there some way to implement something similar in Scala? I envision something with the following form:
val something =
doto(Something.getInstance) {
x()
y()
z()
}
which will be equivalent to
val something = Something.getInstance
something.x()
something.y()
something.z()
Might it be possible using scala.util.DynamicVariable
s?
Note that with factory methods, like Something.getInstance
, it is not possible to use the common Scala pattern
val something =
new Something {
x()
y()
z()
}
I don't think there is such a thing built-in in the library but you can mimic it quite easily:
def doto[A](target: A)(calls: (A => A)*) =
calls.foldLeft(target) {case (res, f) => f(res)}
Usage:
scala> doto(Map.empty[String, Int])(_ + ("a" -> 1), _ + ("b" ->2))
res0: Map[String,Int] = Map(a -> 1, b -> 2)
scala> doto(Map.empty[String, Int])(List(_ + ("a" -> 1), _ - "a", _ + ("b" -> 2)))
res10: Map[String,Int] = Map(b -> 2)
Of course, it works as long as your function returns the proper type. In your case, if the function has only side effects (which is not so "scalaish"), you can change doto
and use foreach
instead of foldLeft
:
def doto[A](target: A)(calls: (A => Unit)*) =
calls foreach {_(target)}
Usage:
scala> import collection.mutable.{Map => M}
import collection.mutable.{Map=>M}
scala> val x = M.empty[String, Int]
x: scala.collection.mutable.Map[String,Int] = Map()
scala> doto(x)(_ += ("a" -> 1), _ += ("a" -> 2))
scala> x
res16: scala.collection.mutable.Map[String,Int] = Map(a -> 2)
In Scala, the "typical" way to do this would be to chain "tap" or "pipe" methods. These are not in the standard library, but are frequently defined as so:
implicit class PipeAndTap[A](a: A) {
def |>[B](f: A => B): B = f(a)
def tap[B](f: A => B): A = { f(a); a }
}
Then you would
(new java.util.HashMap[String,Int]) tap (_.put("a",1)) tap (_.put("b",2))
This is not as compact as the Clojure version (or as compact as Scala can be), but it is about as close to canonical as one is likely to get.
(Note: if you want to minimize run-time overhead for adding these methods, you can make a
a private val
and have PipeAndTap
extend AnyVal
; then this will be a "value class" which is only converted into a real class when you need an object to pass around; just calling a method doesn't actually require class creation.)
(Second note: in older versions of Scala, implicit class
does not exist. You have to separately write the class and an implicit def
that converts a generic a
to a PipeAndTap
.)
I think, that the closest would be to import this object's members in scope:
val something = ...
import something._
x()
y()
z()
In this post you can find another example (in section "Small update about theoretical grounds"):
http://hacking-scala.posterous.com/side-effecting-without-braces
Also small advantage with this approach - you can import individual members and rename them:
import something.{x, y => doProcessing}
More simple I guess:
val hm = Map [String, Int] () + ("a"-> 1) + ("b"-> 2)
Your sample
val something =
doto (Something.getInstance) {
x()
y()
z()
}
doesn't look very functional, because - what is the result? I assume you're side effecting.
Something.x().y().z()
could be a way if each call produces the type where the next function can act on.
z(y(x(Something)))
another kind of producing a result.
And there is the andThen
method to chain method calls on collections, you might want to have a look at.
For your Map-example, a fold-left is another way to go:
val hm = Map [String, Int] () + ("a"-> 1) + ("b"-> 2)
val l = List (("a", 8), ("b", 7), ("c", 9))
(hm /: l)(_ + _)
// res8: scala.collection.immutable.Map[String,Int] = Map(a -> 8, b -> 7, c -> 9)
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