scalashapeless

How to zip values with types? Shapeless


I have a HList of coproducts that looks like this:

(A :+: B :+: CNil) :: (Foo :+: Bar :+: CNil) :: HNil

This HList will be a value in my program. E.g.,:

val myhlist: (A :+: B :+: CNil) :: (Foo :+: Bar :+: CNil) :: HNil = ???

I have another HList of coproducts, that looks like this:

(A :+: CNil) :: (Bar :+: CNil) :: HNil

However, this HList will not be a value throughout the program; it only exists in the type level.

I want to zip these two HLists with this zipping function:

def zipFn[A <: Coproduct, B <: Coproduct](a: A)(implicit basis: Basis[A, B]) = {
  a.deembed[B]
}

A is an element from the former HList (which will have values throughout the program), and B is an element from the latter HList. I want to zip values (As) with types (Bs).

Might be a very simple question but somehow I am troubled with this?

Any pointers?

--

My current idea was to use a curried polymorphic function in Shapeless; something like:

object myZipFn extends Poly1 {
  implicit def default[A <: Coproduct, B <: Coproduct](implicit basis: Basis[A, B]) = at[A] { a => a.deembed[B] }
}

val curried = Poly.curried(myZipFn)

but doesn't make much sense since default asks for A and B at the same time.

My idea would be to take in B (the type with no value) generically first, then return a new function that asks for A as a value, and then we have both.

Thanks


Solution

  • .zipWith accepts Poly2, not Poly1. So myZipFn should be

    object myZipFn extends Poly2 {
      implicit def default[A <: Coproduct, B <: Coproduct](implicit
        basis: Basis[A, B]
      ): Case.Aux[A, B, basis.Out] = at[A, B] { (a, _) => a.deembed[B] }
    }
    

    At the not necessary 2nd argument of .zipWith (because you have the 2nd HList only on type level, not on value level) you can substitute an HList of nulls (but typed properly)

    val myhlist: (A :+: B :+: CNil) :: (Foo :+: Bar :+: CNil) :: HNil = ???
    type hlist1 = (A :+: CNil) :: (Bar :+: CNil) :: HNil
    
    object nullPoly extends Poly0 {
      implicit def default[A]: Case0[A] = at(null.asInstanceOf[A])
    }
    
    val nullHlist: hlist1 = FillWith[nullPoly.type, hlist1].apply()
    
    myhlist.zipWith(nullHlist)(myZipFn)
    

    Alternatively, if you don't want to have an HList of nulls, you'll have to define custom Zip/ZipWith type class for value/type-level 1st argument and type-level 2nd argument.

    Other use cases of FillWith/nullPoly trick:

    How to obtain all possible members of a coproduct

    Scala - looping over case class names to use as type parameters

    How to get field names and field types from a Generic Type in Scala?

    Is it possible to have a variable type list in Scala?

    Get case class field's name and type with shapeless

    How to pass into generic type using a Symbol or Type object into a generic typed function?