catnip icon indicating copy to clipboard operation
catnip copied to clipboard

"knownDirectSubclasses observed before subclass" when deriving for sealed trait

Open nadavwr opened this issue 4 years ago • 1 comments

Deriving for a case class works great:

@Semi(Show) case class Foo(a: Int) 

Derivation for ADT's is a bit brittle though:

@Semi(Show) sealed trait Tree
case class Node(nodes: List[Tree]) extends Tree
case class Leaf(label: String) extends Tree
// 👎Could not derive an instance of Show[Tree]
//implicit val _derived_cats_Show: cats.Show[Tree] = cats.derived.semi.show[Tree]
//knownDirectSubclasses of Tree observed before subclass Node registered
//knownDirectSubclasses of Tree observed before subclass Leaf registered

That's a pity—it's the first thing people will try (at least that's what I went for).

Naturally, the same error occurs with direct use of Kittens:

sealed trait Tree
object Tree {
  implicit val showTree: Show[Tree] = cats.derived.semi.show
}
case class Node(nodes: List[Tree]) extends Tree
case class Leaf(label: String) extends Tree
// 👎Could not derive an instance of Show[A]
//knownDirectSubclasses of Tree observed before subclass Node registered
//knownDirectSubclasses of Tree observed before subclass Leaf registered

Placing the companion object below the subclasses solves the problem:

sealed trait Tree
case class Node(nodes: List[Tree]) extends Tree
case class Leaf(label: String) extends Tree
object Tree {
  implicit val showTree: Show[Tree] = cats.derived.semi.show
}
// 👍 

But the @Semi annotation only works on the case class itself:

sealed trait Tree
case class Node(nodes: List[Tree]) extends Tree
case class Leaf(label: String) extends Tree
@Semi(Show) object Tree
// 👎@Semi can only annotate class, got: ...

How about allowing @Semi to be placed on the companion? Adding a bit of docs around this pitfall might also help.


P.s. The first two things I tried failed, but the following alternatives work fine:

case class Node(nodes: List[Tree]) extends Tree
case class Leaf(label: String) extends Tree
@Semi(Show) sealed trait Tree
@Semi(Show) sealed trait Tree
object Tree {
  case class Node(nodes: List[Tree]) extends Tree
  case class Leaf(label: String) extends Tree
}
sealed trait Tree
case class Node(nodes: List[Tree]) extends Tree
case class Leaf(label: String) extends Tree
@Semi(Show) object Tree

nadavwr avatar Dec 23 '19 11:12 nadavwr