milessabin / shapeless

Generic programming for Scala
Apache License 2.0
3.39k stars 533 forks source link

encoder derivation for argonaut-shapeless fails on custom Enums #369

Closed clayrat closed 9 years ago

clayrat commented 9 years ago

Trying to use argonaut-shapeless to derive case class Json encoders for some autogenerated by ScalaBuff code, I've ran into a following issue:

import argonaut._, Argonaut._, Shapeless._

object Tool {

  // start of autogenerated code 
  trait Enum {
    type EnumVal <: Value
    protected trait Value { self: EnumVal =>
      val name: String
    }
  }

  object BarEnum extends Enum {
    sealed trait EnumVal extends Value
    val BarA = new EnumVal { val name = "A" }
    val BarB = new EnumVal { val name = "B" }
    val BarC = new EnumVal { val name = "C" }
  }

  final case class Foo(barEnum: Option[BarEnum.EnumVal])
  // end of autogenerated code

  def main(args: Array[String]) {

    implicit def BarEnumValEncodeJson: EncodeJson[BarEnum.EnumVal] =
      EncodeJson((s: BarEnum.EnumVal) => jString(s.name))

    val fooEncoder = implicitly[EncodeJson[Foo]]

  }

}

This code fails to compile with the following errors:

[error] Cannot set NoSymbol's name to <none>
[error] Cannot set NoSymbol's name to <none>
[error] Cannot set NoSymbol's name to <none>
[error] Tool.scala:26: could not find implicit value for parameter e: argonaut.EncodeJson[Tool.Foo]
[error]     val fooEncoder = implicitly[EncodeJson[Foo]]

There are several ways to make it compile:

Given that in real code everything outside of main is autogenerated, only the last option is a viable workaround.

milessabin commented 9 years ago

Could you try with the elements of the enumeration declared as objects? ie.,

object BarA extends EnumVal { val name = "A" }

I'm surprised that it works for two elements.

milessabin commented 9 years ago

Actually, could you open a feature request against shapeless to add Generic support for things like,

sealed trait EnumVal
val BarA = new EnumVal { val name = "A" }
val BarB = new EnumVal { val name = "B" }
val BarC = new EnumVal { val name = "C" }

I can't make any promises, but if it's possible it looks like it would be useful.

clayrat commented 9 years ago

It seems to work when you declare enum vals as objects.

Sorry, I don't think I have the rights to add labels to issues.