Clojure提供了一个调用的宏doto
,它接受其参数和函数列表,并基本上调用每个函数,在(计算的)参数前面加上:
(doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2))
-> {a=1, b=2}
Run Code Online (Sandbox Code Playgroud)
有没有办法在Scala中实现类似的东西?我设想了以下形式的东西:
val something =
doto(Something.getInstance) {
x()
y()
z()
}
Run Code Online (Sandbox Code Playgroud)
这相当于
val something = Something.getInstance
something.x()
something.y()
something.z()
Run Code Online (Sandbox Code Playgroud)
可能使用scala.util.DynamicVariable
s?
请注意,使用工厂方法时,Something.getInstance
无法使用常见的Scala模式
val something =
new Something {
x()
y()
z()
}
Run Code Online (Sandbox Code Playgroud)
Nic*_*las 12
我不认为库中内置了这样的东西,但你可以很容易地模仿它:
def doto[A](target: A)(calls: (A => A)*) =
calls.foldLeft(target) {case (res, f) => f(res)}
Run Code Online (Sandbox Code Playgroud)
用法:
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)
Run Code Online (Sandbox Code Playgroud)
当然,只要您的函数返回正确的类型,它就会起作用.在您的情况下,如果该函数只有副作用(不是那么"scalaish"),您可以更改doto
并使用foreach
而不是foldLeft
:
def doto[A](target: A)(calls: (A => Unit)*) =
calls foreach {_(target)}
Run Code Online (Sandbox Code Playgroud)
用法:
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)
Run Code Online (Sandbox Code Playgroud)
在Scala中,执行此操作的"典型"方法是链接"tap"或"pipe"方法.这些不在标准库中,但通常定义如下:
implicit class PipeAndTap[A](a: A) {
def |>[B](f: A => B): B = f(a)
def tap[B](f: A => B): A = { f(a); a }
}
Run Code Online (Sandbox Code Playgroud)
那你会的
(new java.util.HashMap[String,Int]) tap (_.put("a",1)) tap (_.put("b",2))
Run Code Online (Sandbox Code Playgroud)
这不像Clojure版本那么紧凑(或者像Scala那样紧凑),但它与人们可能获得的规范差不多.
(注意:如果你希望尽量减少添加这些方法运行时的开销,可以使a
一个private val
并有PipeAndTap
扩展AnyVal
,那么这将是一个"值类"这是只有转化为真正的类时,你需要一个对象来绕过;只是调用一个方法实际上并不需要创建类.)
(第二个注释:在旧版本的Scala中,implicit class
不存在.您必须单独编写类implicit def
,并将泛型a
转换为PipeAndTap
.)