首页 > 解决方案 > How to obtain all possible members of a coproduct

问题描述

I've been trying to list all the members of a coproduct type. This answer is really close to what I want to achive:

sealed trait Traity
case object Foo  extends Traity
case class Bar() extends Traity
case class Baz() extends Traity

import shapeless._

class NameHelper[A] {
  def apply[C <: Coproduct, K <: HList]()(
    implicit
    gen: LabelledGeneric.Aux[A, C],
    keys: ops.union.Keys.Aux[C, K],
    toSet: ops.hlist.ToTraversable.Aux[K, Set, Symbol]): Set[String] = toSet(keys()).map(_.name)
}

def names[A] = new NameHelper[A]

names[Traity]()

With the expected output:

res0: Set[String] = Set(Bar, Baz, Foo)

But my source type is not a sealed trait, It's a raw shapeless coproduct. I've tried the following approach, but it does not compile:

type ISB = Int :+: String :+: Boolean :+: CNil

class NameHelper2[A <: Coproduct] {
  def apply[K <: HList]()(
    implicit
  keys: ops.union.Keys.Aux[A, K],
  toSet: ops.hlist.ToTraversable.Aux[K, Set, Symbol]): Set[String] = toSet(keys()).map(_.name)
}

def names2[A <: Coproduct] = new NameHelper2[A]

names2[ISB]()

The outcome is a missing implicit value:

could not find implicit value for parameter keys: shapeless.ops.union.Keys.Aux[ISB,K]
names2[ISB]()

What I want is the possible coproduct members: Set(Int, String, Boolean)

Thanks in advance.

标签: scalaunionshapeless

解决方案


It's not so close. There you extract existing keys, here you create textual representation of types.

You can do

class NameHelper2[C <: Coproduct] {
  def apply[L <: HList, L1 <: HList]()(
    implicit
    toHList: ops.coproduct.ToHList.Aux[C, L],
    fillWith: ops.hlist.FillWith[nullPoly.type, L],
    mapper: ops.hlist.Mapper.Aux[typeablePoly.type, L, L1],
    toSet: ops.hlist.ToTraversable.Aux[L1, Set, String]): Set[String] = toSet(mapper(fillWith()))
}

object nullPoly extends Poly0 {
  implicit def cse[X]: Case0[X] = at(null.asInstanceOf[X])
}

object typeablePoly extends Poly1 {
  implicit def cse[X](implicit typeable: Typeable[X]): Case.Aux[X, String] = at(_ => typeable.describe)
}

推荐阅读