Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I get a compile-time list of all of the case objects which derive from a sealed parent in Scala?

Tags:

scala

As has been discussed many times on SO, a Scala match will warn you if you don't exhaustively list all of the types deriving from a sealed class.

What I want is a compile-time generated Iterable of the case objects deriving from a particular parent. Alternatively, I'd be happy with a way to make the compiler tell me I don't have all of the necessary types in some Iterable. I don't want a run-time, reflection-based approach.

As an example of the second approach, I'd like to have the following rough code generate a compile error where indicated.

sealed trait Parent case object A extends Parent case object B extends Parent case object C extends Parent  // I want a compiler error here because C is not included in the Seq() val m = Seq(A, B).map(somethingUseful) 

Feel free to answer by telling me it's not possible. It just seems like it should be possible at some level because the compiler must be doing essentially the same work when determining a match is non-exhaustive.

Thinking about it another way, I'd take something like the Enumeration.values() method except applied to case objects. Certainly, I could add something similar to the code above with a manually maintained list of values to the parent's companion object, but that seems needlessly error-prone when the compiler could do that for me.

// Manually maintained list of values object Parent {      val values = Seq(A, B, C) } 
like image 340
Leif Wickland Avatar asked Aug 22 '12 17:08

Leif Wickland


People also ask

What does sealed do in Scala?

The sealed is a Scala keyword used to control the places where given trait or class can be extended. More concretely, the subclasses and the implementations can be defined only in the same source file as the sealed trait or class.

What is difference between Case class and case object in Scala?

A Scala object represents a class that has exactly one single instance. An object is a singleton as a top-level value. The big difference between object and class is that in object, we cannot assign parameters on constructors.

What is a sealed abstract class Scala?

Scala allows using the sealed modifier for traits, classes, and abstract classes. In the context of sealed, a class, trait, or abstract class functions the same except for the normal differences between classes and traits — for instance, an abstract class can receive parameters, and traits cannot.

What is sealed trait?

Sealed traits are closed: they only allow a fixed set of classes to inherit from them, and all inheriting classes must be defined together with the trait itself in the same file or REPL command.


1 Answers

Update. Since 2.10.0-M7 we're exposing the methods mentioned in this answer as a part of public API. isSealed is ClassSymbol.isSealed and sealedDescendants is ClassSymbol.knownDirectSubclasses.

This is not going to be an answer to your question.

But, if you're willing to settle for something more like Enumeration.values(), and you're using a recent milestone of 2.10, and you're willing to muck about with some ugly casting-to-internal-APIs business, you can write the following:

import scala.reflect.runtime.universe._  def sealedDescendants[Root: TypeTag]: Option[Set[Symbol]] = {   val symbol = typeOf[Root].typeSymbol   val internal = symbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol]   if (internal.isSealed)     Some(internal.sealedDescendants.map(_.asInstanceOf[Symbol]) - symbol)   else None } 

Now if you've got a hierarchy like this:

object Test {   sealed trait Parent   case object A extends Parent   case object B extends Parent   case object C extends Parent } 

You can get the type symbols for the members of the sealed type hierarchy like this:

scala> sealedDescendants[Test.Parent] getOrElse Set.empty res1: Set[reflect.runtime.universe.Symbol] = Set(object A, object B, object C) 

It's hideous, but I don't think you're going to get what you actually want without writing a compiler plugin.

like image 59
Travis Brown Avatar answered Oct 19 '22 16:10

Travis Brown