2012-06-20 218 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)) 

在這種情況下,它是不是很實用,但是,作爲功能類型不能容易地推斷出...