2012-06-20 172 views
10

的Clojure提供了一个称为doto宏接受其参数和功能列表,基本上调用每个函数的前面加上(评估)的说法:`doto`斯卡拉

(doto (new java.util.HashMap) (.put "a" 1) (.put "b" 2)) 
-> {a=1, b=2} 

是否有某种方式来实现的东西在斯卡拉类似?我设想的东西有以下形式:

val something = 
    doto(Something.getInstance) { 
    x() 
    y() 
    z() 
    } 

这将等同于

val something = Something.getInstance 
something.x() 
something.y() 
something.z() 

也许它使用scala.util.DynamicVariable S为可能吗?

注意,与工厂方法,如Something.getInstance,则无法使用普通的斯卡拉模式

val something = 
    new Something { 
    x() 
    y() 
    z() 
    } 
+0

作为一个侧面说明,这个问题是一个dup。但是,我找不到原文。 –

回答

12

我不认为有这样内置在库中的事情,但你可以模仿它很容易:

def doto[A](target: A)(calls: (A => A)*) = 
    calls.foldLeft(target) {case (res, f) => f(res)} 

用法:

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) 

当然,只要你的函数返回正确的类型,它就会工作。在你的情况下,如果函数只有副作用(这是不那么“scalaish”),你可以改变doto和使用foreach代替foldLeft

def doto[A](target: A)(calls: (A => Unit)*) = 
    calls foreach {_(target)} 

用法:

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) 
+0

这是一个有趣的方法。我没有想到要用折叠。函数式编程的奇迹... – Ralph

+0

谨防再次,根据您的例子中,'fold'版本将不适合,你就必须坚持'foreach'version – Nicolas

+0

正如我评论到@unknown用户,我“M试图用这种模式来初始化标准Java库的对象,特别是'java.net.URLConnection'返回一个'java.net.HttpConnection.openConnection'。这个API的设计并非考虑到链接。不幸的是,当连接到标准库API,并尝试以功能性风格编写时,通常需要使用kludges。 – Ralph

4

我认为,最近将是在范围上导入该对象的成员:

val something = ... 
import something._ 

x() 
y() 
z() 

在这篇文章中,你可以找到另一个例子(节“有关的理论依据小更新”):

http://hacking-scala.posterous.com/side-effecting-without-braces


还小的优势这种方法 - 你可以导入单个成员并重新命名:

import something.{x, y => doProcessing} 
1

更简单我想:

val hm = Map [String, Int]() + ("a"-> 1) + ("b"-> 2) 

你的样品

val something = 
    doto (Something.getInstance) { 
    x() 
    y() 
    z() 
    } 

看起来不太实用,因为 - 结果是什么?我认为你是副作用。

Something.x().y().z() 

可能是一种方式,如果每次调用都会产生下一个函数可以执行的类型。

z(y(x(Something))) 

另一种产生结果。

还有就是andThen方法链方法调用集合,你可能想看看。

您的地图,例如,折叠左边是另一种方式去:

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) 
+0

它不是很实用,但是再一次,Java标准库也不是。该示例适用于库中的许多功能,如初始化Swing组件,URL连接(从HttpConnection获取)等。 – Ralph

5

在Scala中,“典型”的方式来做到这将是产业链“龙头”和“管”的方法。这些都不是在标准库中,但经常被定义为这样:

implicit class PipeAndTap[A](a: A) { 
    def |>[B](f: A => B): B = f(a) 
    def tap[B](f: A => B): A = { f(a); a } 
} 

,那么你会

(new java.util.HashMap[String,Int]) tap (_.put("a",1)) tap (_.put("b",2)) 

这不是一样紧凑Clojure的版本(或紧凑斯卡拉可) ,但是它可能会达到与标准接近的程度。

(注:如果你希望尽量减少添加这些方法运行时的开销,可以使a一个private val并有PipeAndTap延长AnyVal,那么这将是一个“值类”这是只有转化为现实上课的时候,你需要一个对象来绕过;只是调用的方法实际上并不需要创建类)

(二注意:在旧版本的斯卡拉,implicit class不存在您必须单独写类和implicit def,其转换通用aPipeAndTap。)

0

嗯,我能想到的做这件事的方法有两种:传递字符串作为参数,并具有宏观变化的串并编译它,或者干脆导入方法。如果斯卡拉有无类型的宏,也许它们也可以使用 - 因为它没有它们,我不打算推​​测它。

无论如何,我要留给别人宏替代品。导入的方法很简单:

val map = collection.mutable.Map[String, Int]() 
locally { 
    import map._ 
    put("a", 1) 
    put("b", 2) 
} 

注意locally不会做任何事情,只是限制它的map成员都是进口的范围。以链的几个动作

0

一个非常基本的方法是功能组成:

val f:Map[Int,String]=>Map[Int,String] = _ + (1 -> "x") 
val g:Map[Int,String]=>Map[Int,String] = _ + (2 -> "y") 
val h:Map[Int,String]=>Map[Int,String] = _ + (3 -> "z") 

(h compose g compose f)(Map(42->"a")) 
// Map[Int,String] = Map((42,a), (1,x), (2,y), (3,z)) 

在这种情况下,它是不是很实用,但是,作为功能类型不能容易地推断出...