2
votes

I have written automatic type class derivation in order to automatically generate elasticsearch Json mapping for case classes. For that I am using the TypeClass type class in shapeless. The problem I have is that many fields in the case classes we use are Scala enumerations. For example

  object ConnectionState extends Enumeration {
    type ConnectionState = Value
    val ordering, requested, pending, available, down, deleting, deleted, rejected = Value
  }

Or

object ProductCodeType extends Enumeration {
    type ProductCodeType = Value
    val devpay, marketplace = Value
  }

It seems I have to define a specific implicit instance for every Enumeration that is defined in order that the automatic derivation will pick it up (e.g. for both ConnectionState and ProductCodeType). I cannot have one implicit def for Enumeration such as

  implicit def enumerationMapping: MappingEncoder[Enumeration] = new MappingEncoder[Enumeration] {
    def toMapping = jSingleObject("type", jString("text"))
  }

that will work for all enumeration types. I tried making the type class covariant, and a bunch of other things but nothing helped. Any ideas?

Here is the derivation code:

  object Mapping {
    trait MappingEncoder[T] {
      def toMapping: Json
    }
    object MappingEncoder extends LabelledProductTypeClassCompanion[MappingEncoder] {
      implicit val stringMapping: MappingEncoder[String] = new MappingEncoder[String] {
        def toMapping = jSingleObject("type", jString("text"))
      }
      implicit val intMapping: MappingEncoder[Int] = new MappingEncoder[Int] {
        def toMapping = jSingleObject("type", jString("integer"))
      }
      implicit def seqMapping[T: MappingEncoder]: MappingEncoder[Seq[T]] = new MappingEncoder[Seq[T]] {
        def toMapping = implicitly[MappingEncoder[T]].toMapping
      }
      implicit def optionMapping[T: MappingEncoder]: MappingEncoder[Option[T]] = new MappingEncoder[Option[T]] {
        def toMapping = implicitly[MappingEncoder[T]].toMapping
      }
      object typeClass extends LabelledProductTypeClass[MappingEncoder] {
        def emptyProduct = new MappingEncoder[HNil] {
          def toMapping = jEmptyObject
        }

        def product[F, T <: HList](name: String, sh: MappingEncoder[F], st: MappingEncoder[T]) = new MappingEncoder[F :: T] {
          def toMapping = {
            val head = sh.toMapping
            val tail = st.toMapping
            (name := head) ->: tail
          }
        }
        def project[F, G](instance: => MappingEncoder[G], to: F => G, from: G => F) = new MappingEncoder[F] {
          def toMapping = jSingleObject("properties", instance.toMapping)
        }
      }
    }
  }
1

1 Answers

0
votes

I was able to fix the problem by adding additional implicit defs to scope:

implicit def enumerationMapping[T <: Enumeration#Value]: MappingEncoder[T] = new MappingEncoder[T] {
  def toMapping = jSingleObject("type", jString("text"))
}
implicit def enumerationSeqMapping[T <: Enumeration#Value]: MappingEncoder[Seq[T]] = new MappingEncoder[Seq[T]] {
  def toMapping = jSingleObject("type", jString("text"))
}

The second implicit was needed as some of the case classes had members of type Seq[T] where T is some enumeration type.