Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Decoding Case Class w/ Tagged Type

Given:

Given the following on Ammonite:

@ import $ivy.`io.circe::circe-core:0.9.0` 

@ import $ivy.`io.circe::circe-generic:0.9.0`                   

@ import $ivy.`com.chuusai::shapeless:2.3.3` 

@ import shapeless.tag 
import shapeless.tag

@ trait Foo 
defined trait Foo

@ import io.circe._, io.circe.generic.semiauto._ 
import io.circe._, io.circe.generic.semiauto._

@ import shapeless.tag.@@ 
import shapeless.tag.@@

@ implicit def taggedTypeDecoder[A, B](implicit ev: Decoder[A]): Decoder[A @@ B] = 
    ev.map(tag[B][A](_)) 
defined function taggedTypeDecoder

Given a Foo:

@ case class F(x: String @@ Foo)  
defined class F

I can summon an Decoder[String @@ Foo]:

@ Decoder[String @@ Foo] 
res17: Decoder[String @@ Foo] = io.circe.Decoder$$anon$21@16b32e49

But not a F:

@ deriveDecoder[F] 
cmd18.sc:1: could not find Lazy implicit value of type io.circe.generic.decoding.DerivedDecoder[ammonite.$sess.cmd16.F]
val res18 = deriveDecoder[F]
                         ^
Compilation Failed

How can I get a Decoder[F]?

like image 997
Kevin Meredith Avatar asked Jan 09 '18 18:01

Kevin Meredith


1 Answers

This is a bug in shapeless' Lazy - milessabin/shapeless#309

I have a PR that makes your example compile - milessabin/shapeless#797 (I checked with publishLocal)

Basically the problem in Lazy is that it expands type aliases too eagerly (A @@ B is a type alias for A with Tagged[B]) which in turn triggers a Scala bug - scala/bug#10506

The Scala bug doesn't have a clear solution in sight. It's another incarnation of the subtyping vs parametric polymorphism problem that complicates type inference. The gist of it is that Scala has to perform subtype checking and type inference at the same time. But when we put some type variables like A and B in a refined type like A with Tagged[B] (actually circe ends up looking for a FieldType[K, A with Tagged[B]] where FieldType is yet another type alias hiding a refined type), subtyping has to be checked for each component individually. This means that the order in which we choose to check the components determines how the type variables A and B will be constrained. In some cases they end up over- or under-constrained and cannot be inferred correctly.

Apropo, the shapeless tests show a workaround, but I don't think it applies to circe, because it's using some kind of macro rather than doing vanilla typeclass derivation.

Long story short you can:

  1. Wait for a shapeless (please upvote #797) and subsequent circe release
  2. Not use tagged types =/
  3. Try to use a different encoding without refined or structural types - maybe alexknvl/newtypes? (I haven't tried)
like image 78
g.krastev Avatar answered Nov 06 '22 00:11

g.krastev