Scala的`doto`

Ral*_*lph 10 scala

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.DynamicVariables?

请注意,使用工厂方法时,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)

  • 如果你使用varargs而不是使用'TraversableOnce`,你可以在你的呼叫站点删除`List(...)`."x"也可以是"val" (2认同)

Rex*_*err 5

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