Looking through the Scala source code, I stumbled across Enumeration.scala
:
abstract class Enumeration(initial: Int, names: String*) extends Serializable {
thisenum =>
def this() = this(0)
def this(names: String*) = this(0, names: _*)
/* Note that `readResolve` cannot be private, since otherwise
the JVM does not invoke it when deserializing subclasses. */
protected def readResolve(): AnyRef = thisenum.getClass.getField("MODULE$").get()
// ... SNIP ...
}
What is the the thisenum =>
for? I couldn't find any info in the "Programming in Scala" book.
Arrow is a type class for modeling composable relationships between two types. One example of such a composable relationship is function A => B ; other examples include cats. data. Kleisli (wrapping an A => F[B] , also known as ReaderT ), and cats.
=> is syntactic sugar for creating instances of functions. Recall that every function in scala is an instance of a class. For example, the type Int => String , is equivalent to the type Function1[Int,String] i.e. a function that takes an argument of type Int and returns a String .
Not everything is an object in Scala, though more things are objects in Scala than their analogues in Java. The advantage of objects is that they're bags of state which also have some behavior coupled with them. With the addition of polymorphism, objects give you ways of changing the implicit behavior and state.
Instead, Scala has singleton objects. A singleton is a class that can have only one instance, i.e., Object. You create singleton using the keyword object instead of class keyword.
The Programming in Scala 2d edition introduce the concept of self type in the section 29.4 "Splitting modules into trait":
The SimpleFoods trait could look as:
trait SimpleFoods {
object Pear extends Food("Pear")
def allFoods = List(Apple, Pear)
def allCategories = Nil
}
So far so good, but unfortunately, a problem arises if you try to define a SimpleRecipes trait like this:
trait SimpleRecipes { // Does not compile
object FruitSalad extends Recipe(
"fruit salad",
List(Apple, Pear), // Uh oh
"Mix it all together."
)
def allRecipes = List(FruitSalad)
}
The problem here is that
Pear
is located in a different trait from the one that uses it, so it is out of scope.
The compiler has no idea thatSimpleRecipes
is only ever mixed together withSimpleFoods
.
There is a way you can tell this to the compiler, however. Scala provides the self type for precisely this situation.
Technically, a self type is an assumed type for this whenever this is mentioned within the class.
Pragmatically, a self type specifies the requirements on any concrete class the trait is mixed into.
If you have a trait that is only ever used when mixed in with another trait or traits, then you can specify that those other traits should be assumed.
In the present case, it is enough to specify a self type ofSimpleFoods
, as shown:
trait SimpleRecipes {
this: SimpleFoods =>
object FruitSalad extends Recipe(
"fruit salad",
List(Apple, Pear), // Now Pear is in scope
"Mix it all together."
)
def allRecipes = List(FruitSalad)
}
Given the new self type,
Pear
is now available.
Implicitly, the reference toPear
is thought of asthis.Pear
.
This is safe, because any concrete class that mixes inSimpleRecipes
must also be a subtype ofSimpleFoods
, which means thatPear
will be a member.
Abstract subclasses and traits do not have to follow this restriction, but since they cannot be instantiated with new, there is no risk that thethis.Pear
reference will fail
It's a self type. See section 29.4 of Programming in Scala Second Edition. I don't think it was covered on the first edition, and I don't have one around to lookup anyway.
All that did, in this example, was make sure thisenum
would refer to Enumeration
's this
from any inner of Enumeration
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With