2014-03-05 49 views
9

我想以編程方式編寫幾個函數。如果這些功能都是同一類型的,我可以做到以下幾點:用不同類型的函數減少和然後

def a(x: Int): Int = x+1 
def b(y: Int): Int = y/2 
def c(z: Int): Int = z*4 
val f1 = (a _) andThen (b _) andThen (c _) 
val f2 = List((a _), (b _), (c _)).reduce(_ andThen _) 

在這一點f1f2都是一樣的東西,這編譯因爲定義f2ListList[Function1[Int,Int]]

但是,如果我想使用相同的基本縮減技術將不同類型的多個兼容功能鏈接在一起,則會發生錯誤。

def d(x: Double): Int = x.toInt 
def e(y: Int): String = y.toString 
def f(z: String): Double = z.toDouble*4 

//Works fine 
val f3 = (d _) andThen (e _) andThen (f _) 

//Doesn't compile 
val f4 = List((d _), (e _), (f _)).reduce(_ andThen _) 

第二個選項不編譯,因爲它定義f4名單推斷爲List[Function1[Any,Any]],但我想不通,如果那裏有一個乾淨的類型安全的方式,採取的職能的有序集合形成Function1[A,B],Function1[B,C],Function1[C,D],...,Function1[X,Y]並將它們粘合在一起,形成Function1[A,Y]

任何想法?

+0

我不相信'和Then'可以確信它在列表中的正確位置。爲了更清楚地說明,在'f3'表達式中考慮兩個'和'運算符:第一個'和Then'與第二個'和Then'是不同的操作,因爲它使用不同的函子作爲它的參數。由於推理鏈的關係,鏈只能真正起作用。 List()結構完全繞過了所有這些。 –

+0

我認爲這可以用不成形的''HList'來完成。當我有時間時,我會盡力去玩。 – ghik

回答

13

這裏有兩個問題。第一個(正如你所指出的)是列表有一個單一的元素類型,它將被推斷爲它所包含的元素類型的最小上限,在這種情況下,這是非常無聊和無用的String with Int with Double => Any。正如我將在第二部分中展示的,異構列表提供瞭解決這部分問題的一種方法。

第二個問題是_ andThen _不夠多態(正如Bob Dalgleish在上面的評論中指出的那樣)。 reduce的參數將是一個帶有具體輸入類型和具體輸出類型的函數,所以即使我們有一個異構列表,我們也無法通過Scala標準庫中的Function來減少它 - 我們需要一個polymorphic function value代替。

幸運的是(如果你真的想在Scala中做這樣的事情),有一個很好的庫叫做Shapeless,它提供了異類列表和多態函數的很好實現。例如,你可以寫:

def d(x: Double): Int = x.toInt 
def e(y: Int): String = y.toString 
def f(z: String): Double = z.toDouble * 4 

import shapeless._ 

object andThen extends Poly2 { 
    implicit def functions[A, B, C] = at[A => B, B => C](_ andThen _) 
} 

然後:

scala> val andThenned = HList((d _), (e _), (f _)).reduceLeft(andThen) 
andThenned: Double => Double = <function1> 

scala> andThenned(13.0) 
res0: Double = 52.0 

我認爲這是相當整潔。

+1

我喜歡你如何將代碼和示例與'然後'分開;) –

+1

我是這麼做的,不管信不信!感謝您的注意! –

+0

這真棒 - 我正在尋找。我肯定有一些關於多態函數值和無定形的讀法。 – evanrsparks