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

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.

Related

Find direct known subclasses with type parameter in macro

In short, I need a macro that for a given sealed trait finds all subtypes.
Suppose I have the following hierarchy:
sealed trait T[A]
case class C[A]() extends T[A]
Now, in my macro, I have the WeakTypeTag for e.g. T[String].
Using .asClass.knownDirectSubclasses, I can find C[A], but I don't see a good way to arrive at type C[String]. I've experimented with .substituteTypes(..., ...) but without success.
I've figured it out.
The answer is using appliedType(..., ...) included in import c.universe._.

If I create a class in Scala, does it implicitly extends AnyVal, AnyRef or both?

If I write a class in Scala, does it automatically extends AnyVal, AnyRef or both?
If I write a class in Scala, does it automatically extends AnyVal, AnyRef or both?
It should be obvious that it cannot automatically extend both because a Scala class can only extend one class, not two.
It should also be obvious that it cannot automatically extend AnyVal because classes that extend AnyVal must obey some specific restrictions which cannot, in general, be satisfied by any random class.
Therefore, the only sensible way it could work is that a class automatically extends AnyRef.
However, we don't have to use common sense, we don't have to use our brains at all, we can just look into the Scala Language Specification [bold emphasis mine]:
5.3 Class Definitions
The extends clause extends sc with mt_1 with … with mt_m can be omitted, in which case extends scala.AnyRef is assumed.

Scala generic: require method to use class's type

I'm pretty new to Scala. I'm trying to write an abstract class whose methods will be required to be implemented on a subclass. I want to use generics to enforce that the method takes a parameter of the current class.
abstract class MySuper{
def doSomething:(MyInput[thisclass]=>MyResult)
}
class MySub extends MySuper{
override def doSomething:(MyInput[MySub]=>MyResult)
}
I know that thisclass above is invalid, but I think it kind of expresses what I want to say. Basically I want to reference the implementing class. What would be the valid way to go about this?
You can do this with a neat little trick:
trait MySuper[A <: MySuper[A]]{
def doSomething(that: A)
}
class Limited extends MySuper[Limited]{
def doSomething(that: Limited)
}
There are other approaches but I find this one works fairly well at expressing what you'd like.

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.)

What is a sealed trait?

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