2015-08-18 15 views
6

假設我有幾個功能:我可以用無形來解決它嗎?

val f1: Int => String 
val f2: (Int, Int) => String 
val f3: (Int, Int, Int) => String 

def fromList1(f: Int => String): List[Int] => Option[String] = 
    _ match {case x::_ => Some(f(x)); case _ => None} 

def fromList2(f: (Int, Int) => String): List[Int] => Option[String] = 
    _ match {case x::y::_ => Some(f(x, y)); case _ => None} 

現在我想編寫一個通用fromList工作如下:

val g1: List[Int] => String = fromList(f1) // as fromList1(f1) 
val g2: List[Int] => String = fromList(f2) // as fromList2(f2) 

我可以做到這一點shapeless

+1

不是列表,但這個想法是一樣的:https://github.com/milessabin/shapeless/wiki/Feature-overview:-shapeless-2.0.0#facilities-for - 抽象多元性 – dk14

回答

4

這可能會幫助:

import shapeless._ 
import syntax.std.traversable._ 
import shapeless.ops.traversable._ 
import syntax.std.function._ 
import ops.function._ 

def fromList[F, L <: HList, R](f: F) 
(implicit fp: FnToProduct.Aux[F, L => R], tr: FromTraversable[L]) = 
    (p: List[Int]) => p.toHList[L] map f.toProduct 

f.toProduct定期轉換功能函數,採用HList爲參數 - 它需要FnToProduct隱含的其實只是調用它。 FnToProduct.Aux是構造函數(由宏生成),它從函數F,hlist類型HList和結果類型R創建FnToProduct。所有這些都是從你通過的f參數中推斷出來的。

最後一個,toHList創建Some(HList)從常規List如果有可能,否則 - None。它使用FromTraversable[L]暗示做到這一點,其中L已經從f推斷出來。 Shapeless2足夠聰明,可以從Tuple識別HList(因爲可能存在隱式轉換)。

例子:

scala> val f1: Int => String = _ => "a" 
f1: Int => String = <function1> 

scala> val f2: (Int, Int) => String = (_, _) => "a" 
f2: (Int, Int) => String = <function2> 

scala> val g1 = fromList(f1) 
g1: List[Int] => Option[String] = <function1> 

scala> g1(List(1)) 
res6: Option[String] = Some(a) 

scala> val g2 = fromList(f2) 
g2: List[Int] => Option[String] = <function1> 

scala> g2(List(1, 2)) 
res7: Option[String] = Some(a) 

scala> g2(List(1)) 
res8: Option[String] = None 
+0

看起來不錯,謝謝。 – Michael

2

是的,你可以

import shapeless._ 
import shapeless.ops.traversable._ 
import syntax.std.traversable._ 
import ops.function._ 

def fromList[F, I <: HList, O](f: F)(implicit 
    ftp: FnToProduct.Aux[F, I => O], 
    ft: shapeless.ops.traversable.FromTraversable[I]): List[Int] => Option[O] = 
    { x: List[Int] => x.toHList[I].map(ftp(f)) } 

說明

我們使用FnToProduct任何FunctionN轉變爲Function1,它接受一個HList作爲唯一的參數。

所以,

Int => String   ----> Int :: HNil => String 
(Int, Int) => String ----> Int :: Int :: HNil => String 
... 

現在我們抽象了該函數的輸入參數的元數,我們可以簡單地轉換List[Int]以適合變換函數的輸入的HList。 爲了執行此轉換,我們需要範圍內的FromTraversable[I]

如果一切成功,我們返回並Option[O]其中O是該函數的返回類型。 如果輸入List的形狀不正確,我們只是不能返回None

使用

@ val f1: Int => String = _.toString 
f1: Int => String = <function1> 

@ val f2: (Int, Int) => String = (_, _).toString 
f2: (Int, Int) => String = <function2> 

@ val fromList1 = fromList(f1) 
fromList1: List[Int] => Option[String] = <function1> 

@ val fromList2 = fromList(f2) 
fromList2: List[Int] => Option[String] = <function1> 

@ fromList1(List(1)) 
res22: Option[String] = Some(1) 

@ fromList2(List(1, 2)) 
res23: Option[String] = Some((1,2)) 

@ fromList1(List()) 
res24: Option[String] = None 
+0

謝謝你的解釋。 – Michael

相關問題