我可以用 Shapeless 解决它吗?
Can I solve it with 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
做到这一点吗?
是的,你可以
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]
范围。
如果一切成功,我们 return 和 Option[O]
其中 O
是函数的 return 类型。
如果输入 List
的形状错误,我们就会 returning 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
这可能有帮助:
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
从常规 List
创建 Some(HList)
,否则 - 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
假设我有几个函数:
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
做到这一点吗?
是的,你可以
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]
范围。
如果一切成功,我们 return 和 Option[O]
其中 O
是函数的 return 类型。
如果输入 List
的形状错误,我们就会 returning 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
这可能有帮助:
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
从常规 List
创建 Some(HList)
,否则 - 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