我可以用Shapeless解决它吗?

时间:2015-08-18 11:47:41

标签: scala shapeless

假设我有一些功能:

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吗?

2 个答案:

答案 0 :(得分: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是构造函数(由宏生成),它从dunction FnToProduct创建F,hlist类型HList和结果类型R。所有这些都是从您传递的f参数推断出来的。

最后一个,toHList如果可能,则从常规Some(HList)创建List,否则为None。它使用FromTraversable[L]隐式来执行此操作,其中L已从f推断出来。 Shapeless2非常智能,可以从HList识别Tuple(因为可能存在隐式转换)。

示例:

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

答案 1 :(得分: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)) }

<强>解释

我们正在使用FnToProductFunctionN转换为仅Function1作为参数的HList

所以,

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

现在我们抽象了函数输入参数的arity,我们可以简单地将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