user48956
user48956

Reputation: 15810

How can I generalize Either to more (and recursive) types?

Scala's Either[A, B] type is a type that's either A or B.

Is there a generalization that lets me define something like the following?

type JSON = Either[String, Int, Float, List[JSON], Map[String, JSON]]

Upvotes: 3

Views: 1122

Answers (2)

jub0bs
jub0bs

Reputation: 66422

Even if you defined your own FiveWay type (an "Either" that would take five type parameters), Scala would not allow you to define

type Json = FiveWay[String, Int, Float, List[Json], Map[String, Json]]

Why? Because a type alias (Json, here) cannot appear on the right-hand side of its own definition. The following example illustrates this fact:

scala> type Json = Either[String, Json]
<console>:11: error: illegal cyclic reference involving type Json
       type Json = Either[String, Json]

More precisely, the Scala specification (section 4.3) tells us that

the type T in a type alias type t[tps] = T may not refer directly or indirectly to the name t

What you want to do here, instead, is apply the Algebraic-Data-Type pattern:

sealed trait Json

final case class JsonString(string: String) extends Json

final case class JsonInt(int: Int) extends Json

final case class JsonFloat(float: Float) extends Json

final case class JsonArray(list: List[Json]) extends Json

final case class JsonObject(map: Map[String, Json]) extends Json

Upvotes: 6

Chris Martin
Chris Martin

Reputation: 30756

The typical approach would be to use a sealed trait with final subtypes.

sealed trait JSON
final case class StringJSON(x: String) extends JSON
final case class IntJSON(x: Int) extends JSON
final case class FloatJSON(x: Float) extends JSON
final case class ListJSON(x: List[JSON]) extends JSON
final case class MapJSON(x: Map[String, JSON]) extends JSON

The feature overview of Shapeless says

shapeless has a Coproduct type, a generalization of Scala's Either to an arbitrary number of choices.

So my first thought would be to try this...

type JSON = String :+: Int :+: Float :+: List[JSON] :+: Map[String, JSON]

... but unfortunately the cyclic reference to JSON doesn't work. And I think this is the problem you're going to run into with any approach you try.


David Barri suggests an approach involving the "cake pattern", though I don't think it's helpful here.

Upvotes: 8

Related Questions