I am trying to create a function, which takes a tuple of higher-kinded types and applies a function to the types within the higher-kinded types.
In the example below, there is a trait Get[A]
which is our higher-kinded type. There is also a tuple of Get's: (Get[String],Get[Int])
as well as function from (String,Int) => Person
.
Scala-3 has a Match-Type called InverseMap which converts the type (Get[String], Get[Int]) into what is essentially the type (String,Int).
So the ultimate goal is to write a function which can take a tuple with any number of Get[_]
types and a function whose input matches the InserveMap types and finally return a Get[_]
, where the wrapped type is the result of the function.
I have attempted to create a function called genericF
below to show the desired behavior, though it may not be correct -- but I think it does at least show the proper intent.
case class Person(name: String, age: Int)
trait Get[A] {
def get: A
}
case class Put[A](get: A) extends Get[A]
val t: (Get[String], Get[Int]) = (Put("Bob"), Put(42))
val fPerson: (String,Int) => Person = Person.apply _
def genericF[T<:Tuple,I<:Tuple.InverseMap[T,Get],B](f: I => B, t: T): Get[B] = ???
val person: Get[Person] = genericF(fPerson, t)
I have set up a Scastie here: https://scastie.scala-lang.org/OleTraveler/QIyNHPLHQIKPv0lgsYbujA/23
Your code is almost compiling already - the only thing is that
fPerson
is of type(String, Int) => Person
instead of((String, Int)) => Person
(taking a tuple instead of 2 separate parameters).The solution below this one is not nice, although it is perhaps more efficient for TupleXXL's. Here's a nicer version with typeclasses (Scastie):
Here's one way you could implement
genericF
usingTuple.InverseMap
(note that I switched the two parameters togenericF
:ExtractG
is to make thePolyFunction
compile, because it requires you apply a type constructor to its type parameter.AllGs
is to verify that the tuple consists only ofGet
s, because as pointed out by Dmytro Mitin, it isn't typesafe otherwise. If it's allGet
s, the type becomesDummyImplicit
, which Scala provides for us. Otherwise, it'sNothing
. I guess it could conflict with other implicit/givenNothing
s in scope, but if you do have one already, you're screwed anyways.Note that this will work only when you have
Get
and will need some modification if you also want it to work for tuples like(Put[String], GetSubclass[Int])
.Travis Stevens, the OP, has managed to get the solution above this one to work without creating
AllGs
, by usingIsMappedBy
. This is what they got (Scastie):And here's one using dependent types, just for fun (Scastie):
I was hoping
Extract
wouldn't compile for tuples like(Put("foo"), 3)
, but unfortunately,AllGs
is still necessary.