That may actually be a good thing. When I define ADTs I always end up stuffing them with methods because it’s the easy thing to do, but then I dislike the result, because it is no longer easy to see the structure of the ADT anymore, with all the method pollution.
I think the better approach (though a bit cumbersome) is to outsource the methods into external traits, which actually also works with the enum syntax:
enum Json {
case Bool(value: Boolean) extends Json with BoolImpl
case Array(items: Seq[Json]) extends Json with ArrayImpl
def foo: Int
}
private trait ArrayImpl { self: Json.Array =>
def foo = items.size
def bar = foo // this method is defined only for Array
}
private trait BoolImpl { self: Json.Bool =>
def foo = if (value) 1 else 0
}
@main def m = {
val j = new Json.Array(Seq(Json.Bool(true)))
assert(j.foo == j.bar)
}
Though again, it’s a little too much boilerplate, especially since it forces specifying the full extends clauses of the ADT cases.