What is a sealed trait? - scala

Sealed classes are described in 'Programming in Scala', but sealed traits are not.
Where can I find more information about a sealed trait?
I would like to know, if a sealed trait is the same as a sealed class?
Or, if not, what are the differences?
When is it a good idea to use a sealed trait (and when not)?

A sealed trait can be extended only in the same file as its declaration.
They are often used to provide an alternative to enums. Since they can be only extended in a single file, the compiler knows every possible subtypes and can reason about it.
For instance with the declaration:
sealed trait Answer
case object Yes extends Answer
case object No extends Answer
The compiler will emit a warning if a match is not exhaustive:
scala> val x: Answer = Yes
x: Answer = Yes
scala> x match {
| case No => println("No")
| }
<console>:12: warning: match is not exhaustive!
missing combination Yes
So you should use sealed traits (or sealed abstract class) if the number of possible subtypes is finite and known in advance. For more examples you can have a look at list and option implementations.

a sealed trait is the same as a sealed class ?
As far as sealed goes, yes. They share the normal differences between trait and class, of course.
Or, if not, what are the differences ?
Moot.
When is it a good idea to use a sealed trait (and when not) ?
If you have a sealed class X, then you have to check for X as well as any subclasses. The same is not true of sealed abstract class X or sealed trait X. So you could do sealed abstract class X, but that's way more verbose than just trait and for little advantage.
The main advantage of using an abstract class over a trait is that it can receive parameters. That advantage is particularly relevant when using type classes. Let's say you want to build a sorted tree, for instance. You can write this:
sealed abstract class Tree[T : Ordering]
but you cannot do this:
sealed trait Tree[T : Ordering]
since context bounds (and view bounds) are implemented with implicit parameters. Given that traits can't receive parameters, you can't do that.
Personally, I prefer sealed trait and use it unless some particular reason makes me use a sealed abstract class. And I'm not talking about subtle reasons, but in-your-face reasons you cannot ignore, such as using type classes.

From the daily-scala blog:
When a trait is "sealed" all of its subclasses are declared within the
same file and that makes the set of subclasses finite which allows
certain compiler checks.

Also I feel the need to point you to the specifications:
The sealed modifier applies to class definitions. A sealed class may not be directly inherited, except if the inheriting template is defined in the same source
file as the inherited class. However, subclasses of a sealed class can be inherited anywhere.
— M. Odersky. The Scala language specification, version 2.8. online, Sept., 2013.

‌‌Briefly:
Sealed traits can only be extended in the same file
List this lets the compiler easily know all possible subtypes
Use sealed traits when the number of possibly subtypes is finite and known in advance
A way of creating something like enum in Java
Help to define algebraic data types (ADTs)
and for more details
Everything about sealed traits in Scala

Traits can also be defined sealed, and only extended by a fixed set of case classes.
The core difference between normal traits and sealed traits can be summarized as follows:
Normal traits are open, so any number of classes can inherit from the trait as long as they provide
all the required methods, and instances of those classes can be used interchangeably via the trait's
required methods.
A normal trait hierarchy makes it easy to add additional sub-classes: just define your class and
implement the necessary methods. However, it makes it difficult to add new methods: a new
method needs to be added to all existing subclasses, of which there may be many.
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.
A sealed trait hierarchy is the opposite: it is easy to add new methods, since a new method can
simply pattern match on each sub-class and decide what it wants to do for each. However, adding
new sub-classes is difficult, as you need to go to all existing pattern matches and add the case to
handle your new sub-class.
As an example
object SealedTraits extends App{
sealed trait Point
case class Point2D(x: Double, y: Double) extends Point
case class Point3D(x: Double, y: Double, z: Double) extends Point
def hypotenuse(p: Point) = p match {
case Point2D(x, y) => math.sqrt(x x + y y)
case Point3D(x, y, z) => math.sqrt(x x + y y + z z)
}
val points: Array[Point] = Array(Point2D(1, 2), Point3D(4, 5, 6))
for (p <- points) println(hypotenuse(p))
// 2.23606797749979
// 8.774964387392123
In general, sealed traits are good for modelling hierarchies where you expect the number of sub-classes
to change very little or not-at-all. A good example of something that can be modeled using sealed trait is
JSON.
A JSON value can only be JSON null, boolean, number, string, array, or dictionary.
JSON has not changed in 20 years, so it is unlikely that anyone will need to extend our JSON
with additional subclasses.
While the set of sub-classes is fixed, the range of operations we may want to do on a JSON blob is
unbounded: parse it, serialize it, pretty-print it, minify it, sanitize it, etc.
Thus it makes sense to model a JSON data structure as a closed sealed trait
hierarchy rather than a normal open trait hierarchy.
sealed trait Json
case class Null() extends Json
case class Bool(value: Boolean) extends Json
case class Str(value: String) extends Json
case class Num(value: Double) extends Json
case class Arr(value: Seq[Json]) extends Json
case class Dict(value: Map[String, Json]) extends Json

Related

Trait that, when mixed in, turns a class into a final class

Is it possible, in Scala, to have a trait that makes the class into which it is mixed in final?
I need to prevent inheriting from a class if this class mixes in a trait, but I'm not sure if this is possible. Maybe I need a macro for that.
Or a trick, like defining a final member in the trait to avoid mixing that trait twice, but this is not what I need to achieve.
Example:
trait MyTrait
class A extends MyTrait
class B extends A
def myFun[T <: MyTrait](t: T)
This function not only requires T to implement/mixin MyTrait, but suppose it contains logic that doesn't work if T is a base class and what is actually passed in as t is a child of T. For example myFun[A](new B).
Clearly this is a corner case but maybe there's a way to ensure/specify that the t passed as parameter must be exactly of type T and not one of its children. I don't know if this is possible, and that would be the best solution.
The closest thing I could find is making sure T is final and the best would be writing something in MyTrait that enforces it.
So my question.
Alternatively, how can I make sure T is a case class?
No, it isn't. Macros won't help either: you can't trigger a macro by extending something, only by an annotation or by calling a method (as far as I know).
What about this approach:
trait MyTrait
class A extends MyTrait
class B extends A
def myFun[T](t: T)(implicit ev: T =:= A) = ()
defined function myFun
# myFun(new A)
# myFun(new B)
Main.scala:61: Cannot prove that cmd2.B =:= cmd1.A.
myFun(new B)
^
Compilation Failed
It's a bit different from what you ask, since it has nothing to do with the trait. It just enforces you can only pass type A into that function, not its children.

Why do we need traits in scala?

So, I was trying to make a finagle server, talk to sentry (not important), and stumbled upon a case, where I needed to inherit from two classes (not traits) at the same time, let's call them class SentryHandler extends Handler and class TwitterHandler extends Handler, and assume, that I need to create MyHandler, that inherits from both of them.
After a moment of stupidity, when I thought it was impossible without using a dreaded "delegation pattern", I found a solution:
trait SentryTrait extends SentryHandler
class MyHandler extends TwitterHandler with SentryTrait
Now, this got me thinking: what is the purpose of having the notion of "trait" to being with? If the idea was to enforce that you can inherit from multiple traits but only a single class, it seems awfully easy to get around. It kinda sounds like class is supposed to be the "main" line of inheritance (that you "extend a class with traits", but that isn't true either: you can extend a trait with (or without) a bunch of other traits, and no class at all.
You cannot instantiate a trait, but the same holds for an abstract class ...
The only real difference I can think of is that a trait cannot have constructor parameters. But what is the significance of that?
I mean, why not? What would the problem with something like this?
class Foo(bar: String, baz: String) extends Bar(bar) with Baz(baz)
Your solution (if I understood correctly) - doesn't work. You cannot multiinherit classes in scala:
scala> class Handler
defined class Handler
scala> class SentryHandler extends Handler
defined class SentryHandler
scala> class TwitterHandler extends Handler
defined class TwitterHandler
scala> trait SentryTrait extends SentryHandler
defined trait SentryTrait
scala> class MyHandler extends TwitterHandler with SentryTrait
<console>:11: error: illegal inheritance; superclass TwitterHandler
is not a subclass of the superclass SentryHandler
of the mixin trait SentryTrait
class MyHandler extends TwitterHandler with SentryTrait
As for the question - why traits, as I see it, this is because traits are stackable in order to solve the famous diamond problem
trait Base { def x: Unit = () }
trait A extends Base { override def x: Unit = { println("A"); super.x}}
trait B extends Base { override def x: Unit = { println("B"); super.x}}
class T1 extends A with B {}
class T2 extends B with A {}
(new T1).x // Outputs B then A
(new T2).x // Outputs A then B
Even though trait A super is Base (for T1) it calls B implementation rather then Base. This is due to trait linearization
So for classes if you extend something - you can be sure that this base will be called next. But this is not true for traits. And that's probably why you do not have trait constructor parameters
The question should rather be: why do we need classes in Scala? Martin Odersky has said that Scala could get by with just traits. We would need to add constructors to traits, so that instances of traits can be constructed. That's okay, Odersky has said that he has worked out a linearization algorithm for trait constructors.
The real purpose is platform interoperability.
Several of the platforms Scala intends to integrate with (currently Java, formerly .NET, maybe in the future Cocoa/Core Foundation/Swift/Objective-C) have a distinct notion of classes, and it is not always easy to have a 1:1 mapping between Scala traits and platform classes. This is different, for example, from interfaces: there is a trivial mapping between platform interfaces and Scala traits – a trait with only abstract members is isomorphic to an interface.
Classes, packages, and null are some examples of Scala features whose main purpose is platform integration.
The Scala designers try very hard to keep the language small, simple, and orthogonal. But Scala is also explicitly intended to integrate well with existing platforms. In fact, even though Scala is a fine language in itself, it was specifically designed as a replacement for the major platform languages (Java on the Java platform, C# on the .NET platform). And in order to do that, some compromises have to be made:
Scala has classes, even though they are redundant with traits (assuming we add constructors to traits), because it's easy to map Scala classes to platform classes and almost impossible to map traits to platform classes. Just look at the hoops Scala has to jump through to compile traits to efficient JVM bytecode. (For every trait there is an interface which contains the API and a static class which contains the methods. For every class the trait is mixed into, a forwarder class is generated that forwards the method calls to trait methods to the static class belonging to that trait.)
Scala has packages, even though they are redundant with objects. Scala packages can be trivially mapped to Java packages and .NET namespaces. Objects can't.
Package Objects are a way to overcome some of the limitations of packages, if we didn't have packages, we wouldn't need package objects.
Type Erasure. It is perfectly possible to keep generic types around when compiling to the JVM, e.g. you could store them in annotations. But third-party Java libraries will have their types erased anyway, and other languages won't understand the annotations and treat Scala types as erased, too, so you have to deal with Type Erasure anyway, and if you have to do it anyway, then why do both?
null, of course. It is just not possible to automatically map between null and Option in any sane way, when interoperating with real-world Java code. You have to have null in Scala, even though we rather wished it weren't there.
The problem with having constructors and state in a trait (which then makes it a class) is with multiple inheritance. While this is technically possible in a hypothetical language, it is terrible for language definition and for understanding the program code. The diamond problem, mentioned in other responses to this question), causes the highest level base class constructor to be called twice (the constructor of A in the example below).
Consider this code in a Scala-like language that allows multiple inheritance:
Class A(val x: Int)
class B extends A(1)
class C extends A(2)
class D extends B, C
If state is included, then you have to have two copies of the value x in class A. So you have two copies of class A (or one copy and the diamond problem - so called due to the diamond shape of the UML inheritance diagram).
Diamond Multiple Inheritance
The early versions of the C++ compiler (called C-Front) had lots of bugs with this and the compiler or the compiled code often crashed handling them. Issues include if you have a reference to B or C, how do you (the compiler, actually) determine the start of the object? The compiler needs to know that in order to cast the object from the Base type (in the image below, or A in the image above) to the Descendant type (D in the image above).
Multiple Inheritance Memory Layout
But, does this apply to traits? The way I understand it, Traits are an easy way to implement composition using the Delegation Pattern (I assume you all know the GoF patterns). When we implement Delegation in any other language (Java, C++, C#), we keep a reference to the other object and delegate a message to it by calling the method in its class. If traits are implemented in Scala internally by simply keeping a reference and calling its method, then traits do exactly the same thing as Delegation. So, why can't it have a constructor? I think it should be able to have one without violating its intent.
The only real difference I can think of is that a trait cannot have constructor parameters. But what is the significance of that? I mean, why not?
Consider
trait A(val x: Int)
trait B extends A(1)
trait C extends A(2)
class D extends B with C
What should (new D {}).x be? Note: there are plans to add trait parameters in Scala 3, but still with restrictions, so that the above is not allowed.

scala - find objects derived from class

Is there a way in scala to get list of all objects(by object I mean scalas object) that derive from specified abstract class?
something like that:
abstract class A
object B extends A //in different file
object C extends A //in different file
def findObjectsDerivingFromA(): Seq[A] //which will give result: Seq(B, C)
I know from here : Can I get a compile-time list of all of the case objects which derive from a sealed parent in Scala?
that it's possible with sealed base trait/class but in my case objects B and C will be pretty complex so I need to have them in different files.
edit:
I've changed method name because previous one was misleading.

Only allow inheritance within the same package

I have a trait which will have a finite number of subclasses. At first I used the sealed modifier and defined the trait and all its subclasses in the same file. After the classes grew, I decided I wanted to refactor them into separate files, however once I did this I could no longer use the sealed modifier on the trait due to the constraint that all subclasses of a sealed trait must be in the same file.
In Scala, Is there a similar way to have a finite number of subclasses for a trait within the same package, across separate files while still gaining the compile-time advantages of sealed traits when doing exhaustive pattern matching?
Sealing is the only way to ask for exhaustiveness checking. However, you could define package-private traits AGuts, BGuts, CGuts, etc. in separate files and then in one file create sealed subclasses A, B, C, etc. as you did before, but mixing in those guts defined elsewhere.
sealed abstract class Thing
class A extends Thing with AGuts
class B extends Thing with BGuts
...

Scala: abstract class constructor parameter vs Trait val members?

I notice that there were several discussions about how to choose between abstract classes and traits, but it seems that none of them focused on the following point. One reason that made me use abstract classes is, they can have constructor parameters, while traits cannot. But why not the following
trait X {
def haha: Int
}
class Y(val haha: Int) extends X
and early definition is even not necessary to get everything work properly (which I worried about). The abstract class version is
abstract class X(haha: Int)
class Y(val haha: Int) extends X(haha)
and I don't like the abstract class version because, when you extend several times, these constructor parameters appear everywhere (maybe someone tells me how to avoid this?).
I am aware that abstract classes interpolate with Java better, and match the "is-a" concept more. Despite these, is there any reason that I should use abstract classes somewhere? Thanks!
The class parameter does not have to be a member (field or def).
abstract class X(haha: Int) {
val hoho = 2 * haha // compile-time constant
}
Similarly, trait initialization order depends on linearization (mix-in order), which is why trait members should be defs and not vals. (And you can always override the def with a val.) With an abstract class, you know who your supers are, and you're defining extension points for subclasses.
But note your abstract class has the val in the wrong place:
abstract class X(val haha: Int)
class Y(haha: Int) extends X(haha)
That is, you would expect X to decide if the param is a val (and it doesn't have to be). Usage of the param in either X or Y could turn it into a field.
Your observation about value params for classes also applies to type params: What a nuisance to pass Foo[A] up the hierarchy. So in Scala, we can have a member type A instead that can remain abstract until defined in a leaf. But this doesn't actually bear on whether to define a trait or a class.
But trait parameters are coming to Scala. (See the Scala bugs for early definitions which are low-priority for this reason.)