Reputation: 137
I would like to create polymorphic function taking a list of various sized tuples as parameter and return a list with the same type. Therefore, the code inside the function should be adapted too. Here an example to explain my problem and my aim :
val l2 = List( ("1","2"), ("3","4"))
val l3 = List( ("1","2","3"), ("4","5","6"))
val k2 = List( ("1","3"), ("5","7"))
val k3 = List( ("1","3","5"), ("7","9","11"))
def convertTuple2 ( l : List[(String,String)] ) : List[(Int,Int)] = {
if (l.contains("1","2")) l.map{ case (a,b) => (a.toInt+10, b.toInt+10) }
else l.map{ case (a,b) => (a.toInt, b.toInt) }
}
def convertTuple3 ( l : List[(String,String,String)] ) : List[(Int,Int,Int)] = {
if (l.contains("1","2","3")) l.map{ case (a,b,c) => (a.toInt+10, b.toInt+10, c.toInt+10)}
else l.map{ case (a,b,c) => (a.toInt, b.toInt, c.toInt) }
}
val l2converted = convertTuple2(l2)
val l3converted = convertTuple3(l3)
val k2converted = convertTuple2(k2)
val k3converted = convertTuple3(k3)
println(l2converted.mkString(",")) //printing (11,12),(13,14)
println(l3converted.mkString(",")) //printing (11,12,13),(14,15,16)
println(k2converted.mkString(",")) //printing (1,3),(5,7)
println(k3converted.mkString(",")) //printing (1,3,5),(7,9,11)
In other words, how to create a unique function being both like convertTuple2(...)
and convertTuple3(...)
and possibly also convertTupleN(...)
please?
It means that l.contains(...)
and the pattern matching { case ... => ... }
elements have to also be adapted according the size of the tuples from the input list.
What would be a simple, elegant and efficient solution?
Upvotes: 0
Views: 251
Reputation: 51723
Try
import shapeless.{HList, Nat, Poly1}
import shapeless.ops.hlist.Tupler
import shapeless.ops.tuple.{Length, Mapper}
import shapeless.ops.nat.ToInt
import shapeless.syntax.nat._
import shapeless.syntax.std.tuple._
import shapeless.poly.->
import shapeless.nat._
object toStringPoly extends Poly1 {
implicit def `case`[N <: Nat](implicit
toInt: ToInt[N]
): Case.Aux[N, String] = at(_ => toInt().toString)
}
object toIntPoly extends (String -> Int)(_.toInt)
object toIntPoly1 extends (String -> Int)(s => s.toInt + 10)
def convertTuple[A <: Product,
N <: Nat,
L <: HList,
B <: Product,
C <: Product](l: List[A])(implicit
length: Length.Aux[A, N],
range: (_1 *--* N) { type Out = L },
tupler: Tupler.Aux[L, B],
toStringMapper: Mapper[B, toStringPoly.type],
toIntMapper: Mapper.Aux[A, toIntPoly.type, C],
toIntMapper1: Mapper.Aux[A, toIntPoly1.type, C],
) : List[C] =
if (l.contains(range().tupled.map(toStringPoly)))
l.map(_.map(toIntPoly1))
else l.map(_.map(toIntPoly))
or
import scala.collection.immutable.IndexedSeq
import shapeless.{HList, Nat, unexpected}
import shapeless.ops.hlist.Tupler
import shapeless.ops.tuple.{Length, Mapper}
import shapeless.ops.nat.ToInt
import shapeless.ops.traversable.ToSizedHList
import shapeless.syntax.std.tuple._
import shapeless.poly.->
object toIntPoly extends (String -> Int)(_.toInt)
object toIntPoly1 extends (String -> Int)(s => s.toInt + 10)
def convertTuple[A <: Product, N <: Nat, L <: HList, C <: Product](l: List[A])(implicit
length: Length.Aux[A, N],
toInt: ToInt[N],
toSizedHList: ToSizedHList.Aux[IndexedSeq, String, N, Option[L]],
tupler: Tupler[L],
toIntMapper: Mapper.Aux[A, toIntPoly.type, C],
toIntMapper1: Mapper.Aux[A, toIntPoly1.type, C],
) : List[C] =
if (l.contains(toSizedHList((1 to toInt()).map(_.toString)).getOrElse(unexpected).tupled))
l.map(_.map(toIntPoly1))
else l.map(_.map(toIntPoly))
Upvotes: 3