Can I solve the problem using Shapeless?

Suppose I have several functions:

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} 

Now I would like to write one general fromList to work as follows:

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

Can I do this using shapeless ?

+6
source share
2 answers

This can help:

 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 converts a regular function into a function that takes an HList as a parameter - this requires FnToProduct implicitly and actually just call it. FnToProduct.Aux is a constructor (generated by a macro) that creates FnToProduct from dunction F , hlist type HList and result type R All of them are derived from the passed parameter F

The latter, toHList creates Some(HList) from a regular List , if possible, otherwise None . It uses FromTraversable[L] implicit to do this, where L already inferred from F Shapeless2 is smart enough to recognize a Tuple HList (since there is probably an implicit conversion).

Example:

 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 
+4
source

Yes, you can

 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)) } 

Explanation

We use FnToProduct to convert any FunctionN to Function1 , which takes an HList as an argument.

So,

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

Now that we have abstracted from the arity of the input parameters for the function, we can simply convert List[Int] to an HList , which is suitable for inputting the converted function. To perform this conversion, we need FromTraversable[I] in the scope.

If everything succeeds, we will return and Option[O] , where O is the return type of the function. If the List entry is in the wrong shape, we just don't return None .

Using

 @ 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 
+2
source

All Articles