Scala 3 - Extract Tuple of wrappers and InverseMap on First Order Type

后端 未结 1 583
遇见更好的自我
遇见更好的自我 2021-01-21 07:27

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, the

相关标签:
1条回答
  • 2021-01-21 08:21

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

    Edit: 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):

    val fPerson: ((String, Int)) => Person = Person.apply _
    
    opaque type Extract[GT <: Tuple, RT <: Tuple] = GT => RT
    given Extract[EmptyTuple, EmptyTuple] = Predef.identity
    given [A, PG <: Tuple, PR <: Tuple](using p: Extract[PG, PR])
       as Extract[Get[A] *: PG, A *: PR] = {
      case h *: t => h.get *: p(t)
    }
    
    def genericF[GT <: Tuple, RT <: Tuple, B](
        f: RT => B,
        t: GT
    )(using extract: Extract[GT, RT]): Get[B] = Put(f(extract(t)))
    

    Edit: Travis Stevens, the OP, has managed to get the solution at the bottom to work without creating AllGs, by using IsMappedBy. This is what they got (Scastie):

    val fPerson: ((String, Int)) => Person = Person.apply _
    
    type ExtractG = [G] =>> G match {
      case Get[a] => a
    }
    
    def extract[T <: Tuple, I <: Tuple.InverseMap[T, Get]](
        t: T
      )(using Tuple.IsMappedBy[Get][T]): I =
      t.map {
        [G] => (g: G) => g.asInstanceOf[Get[_]].get.asInstanceOf[ExtractG[G]]
      }.asInstanceOf[I]
    
    def genericF[T <: Tuple, I <: Tuple.InverseMap[T, Get], B](
        t: T,
        f: I => B
    )(using Tuple.IsMappedBy[Get][T]): Get[B] = Put(f(extract(t)))
    

    And here's one way you could implement genericF using Tuple.InverseMap (note that I switched the two parameters to genericF:

    val fPerson: ((String, Int)) => Person = Person.apply _
    
    type ExtractG = [G] =>> G match {
      case Get[a] => a
    }
    
    type AllGs[T <: Tuple] = T match {
      case EmptyTuple => DummyImplicit
      case Get[_] *: t => AllGs[t]
      case _ => Nothing
    }
    
    def extract[T <: Tuple](t: T)(using AllGs[T]): Tuple.InverseMap[T, Get] =
      t.map {
        [G] => (g: G) => g.asInstanceOf[Get[_]].get.asInstanceOf[ExtractG[G]]
      }.asInstanceOf[Tuple.InverseMap[T, Get]]
    
    def genericF[B](
        t: Tuple,
        f: Tuple.InverseMap[t.type, Get] => B
    )(using AllGs[t.type]): Get[B] = Put(f(extract(t)))
    
    val person: Get[Person] = genericF(t, fPerson)
    

    ExtractG is to make the PolyFunction compile, because it requires you apply a type constructor to its type parameter.

    AllGs is to verify that the tuple consists only of Gets, because as pointed out by Dmytro Mitin, it isn't typesafe otherwise. If it's all Gets, the type becomes DummyImplicit, which Scala provides for us. Otherwise, it's Nothing. I guess it could conflict with other implicit/given Nothings 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]).

    0 讨论(0)
提交回复
热议问题