Class Linearization not working in scala 2.13 - scala

I'm trying to upgrade scala 2.12 to scala 2.13.5
Class linearization is not properly working for me, IntelliJ and Scala compiler throws an error but ideally it should work. Below is the problem.
trait A[E] {
def getObject: E = {
// some implementation
}
}
abstract class B[T](e: Object) {
def getObject: T = {
// some implemntation
}
}
class C[T] extends B[T](null) with A[String] {
def myMethod(): Unit = {
println(this.getObject.someMethodWhichIsNotInStringClassButAvailableInTClass)
}
}
In the above sample program as well, this.getObject is coming from A where I'm expecting it to come from B. Looks like my understanding is wrong. But need to understand this class linearization problem in detail.
Because of the above issue, my code is not compiling as the required method is not available in String class.
Also the same code compiles with scala 2.12 but not with scala 2.13.5.
Another reference - https://stackoverflow.com/a/49832570/819866

This should not compile at all ... and it indeed does not for me (2.13.1):
error: class C inherits conflicting members:
def getObject: T (defined in trait B) and
def getObject: String (defined in trait A)
(note: this can be resolved by declaring an `override` in class C.)
You cannot inherit different methods with the same name.
Also, if your inheritance was actually valid, linearization would still work left to right:
trait A { def foo: Sting }
trait B extends A { def foo = "b" }
trait C extends A { def foo = "c" }
class D extends B with C
println(new D().foo)
This prints "c".

Related

Scala higher kinded type syntax

I'm new to Scala and new to higher kinded types. I want to write something like this;
trait Actor[E[Dependency] <: Event[Dependency]] {
def execute(dependency: Dependency): Unit
}
However I can't refer to the type parameter Dependency in the execute method - the compiler doesn't know it.
I'm aware I can solve it in the following way without HKTs, but this isn't what this question is about;
trait Actor[T <: Event[Dependency], Dependency] {
def execute(dependency: Dependency): Unit
}
I'd like to understand why it doesn't work with the higher kinded type syntax that I've tried? Is it possible at all to express this with HKTs? Is this a valid use-case for a HKT?
EDIT
A bit more information, Event looks like this;
trait Event[Data] {
val payload: Data
}
...and I'm looking to define an event and an actor like this;
case class FooEvent(payload: Foo) extends Event[Foo]
class FooActor extends Actor[FooEvent] {
def execute(dependency: Foo) = {}
}
I will try to improve Alexey's answer - he is right, but he is too short. But I must say that I'm not an expert in HKT and I think I'm only starting to understand the concept.
In your code E[Dependency] is the same as E[_] which says that you have E with some type as parameter. This means that you do not operate over Dependency as type. You also do not operate over E or E[Dependency] as the type either. E is a type constructor, and E[Dependency] is an existential type if I understood it correctly. Please note that
trait Actor[E[D] <: Event[D]] { def execute(d: E) {} }
or
trait Actor[E[D] <: Event[D]] { def execute(d: E[D]) {} }
won't compile either.
You need to specify the proper type as an argument for execute:
trait Actor[E[D] <: Event[D]] { def execute[B](d: E[B]) {} }
This one will compile as E[B] is the type in this context.
Updated:
Please take a look at this code:
trait Event[P] {
val payload: P
}
case class FooEvent(payload: Int) extends Event[Int]
trait BaseActor {
type E = Event[P]
type P
def execute(dep: P)
def runEvent(event: E)
}
trait IntActor extends BaseActor {
type P = Int
}
class FooActor extends IntActor {
def execute(dependency: P) = {}
def runEvent(event: E) = {}
}
val c = new FooActor()
c.runEvent(FooEvent(5))
c.execute(5)
Basically the trick is to define type P which is our Dependency and type E = Event[P] which is always Event[Dependency] then you can use the actor by defining P without defining E as it is already defined. Not sure whether it solves the issue, but it looks like a way to go to me. There are also too many types here, some like IntActor is not necessary. I've put them so it is easier to understand the example
However I can't refer to the type parameter Dependency in the execute method - the compiler doesn't know it.
You can't because it isn't a parameter of Actor. Consider
val actor = new Actor[Event] // E is Event
actor.execute(???) // what argument is this supposed to take? I.e. what is Dependency for Actor[Event]?
UPDATE: Given your edit, the [Dependency, T <: Event[Dependency]] option is precisely what you need. When you write Actor[E[Dependency] <: Event[Dependency]], this means E itself has to have a type parameter. And FooEvent doesn't, so Actor[FooEvent] won't compile.
UPDATE 2: You could try using type members as follows:
trait Event {
type Dependency
val payload: Dependency
}
trait Actor {
type E <: Event
def execute(e: E#Dependency)
}
class Foo
case class FooEvent(payload: Foo) extends Event {
type Dependency = Foo
}
class FooActor extends Actor {
type E = FooEvent
def execute(e: Foo) = {}
}

Trying to understand scala trait

I am new to scala. I don't understand scala traits properly. I have read it is similar to java interfaces but the methods need not be abstract. But how can I declare a scala trait and instantiate it in the following code. BTW, the following code is working fine.
trait fooable {
def foo: Unit = {
println("This is foo")
}
}
object Main {
def main(args: Array[String]): Unit = {
println("This is morking")
val foo = new fooable{}
foo.foo
}
}
Output -
This is morking
This is foo
Thanks in advance.
Scala traits are more general than both Java interfaces and abstract classes.
You can use a trait as an interface, you can use it to store some implementation, you can use it simply to define a common super-type:
trait Message
case class Text(text: String) extends Message
case class Data(data: ByteString) extends Message
Multiple traits can be 'mixed in' a class:
class MyClass extends TraitA with TraitB with TraitC
where any conflict with identically named methods is resolved by the simple rule: the last trait takes precedence. This code:
trait TraitA { def print() { println("A") } }
trait TraitB { def print() { println("B") } }
trait TraitC { def print() { println("C") } }
new MyClass.print()
will print "C".
Scala traits can't be instantiated. You are creating an anonymous class in you example. If you add an abstract method to your trait it will not compile.
Unrelated note:
It is a good practice to write braces "()" in methods with side effects. Your method foo has a side effect: it prints something. So you should write "foo()".
When you instantiate a trait you create an instance of an anonymous class that extends that trait it works the same way as creating anonymous classes of interfaces in java. If you had any unimplemented methods in the trait fooable the compiler would've forced you to implement them on spot when you created your anonymous class.
Indeed your code should work fine and output the same result you mentioned. However the important point to note here is TRAIT CAN NEVER EVER be instantiated. Its the same concept that Java interface can never ever be instantiated.
When scala compiler notice the code foo = new fooable{}, It internally creates an anonymous class which extends your fooable trait and thus it inherits foo() method due to inheritance. See following code snippet:
scala> val foo = new fooable{}
foo: fooable = $anon$1#277c0f21
Thus when you call foo.foo, At runtime it invokes a anonymous's class(i.e. $anon$1#277c0f21) inherited method foo().
The same understanding is true in case of Java as well, Following is perfectly legal java code:
Runnable r = new Runnable() {
public void run() {
System.out.println(" Cat");
}
};
r.run()
Happy learning !
An Scala Trait is abstract and can't be instantiated. In the code above its instantiated as
val foo = new fooable{}
and NOT as
val foo = new fooable()
The curly braces created some anonymous class which is not fooable but an empty one
What are Traits?
Traits are similar to interfaces in any other language. Scala allows traits to be instantiate during definition as well as during construction. For example if we have a trait and a abstract class
trait testTrait {
}
abstract class BaseClass {}
class Derive extends BaseClass with testTrait {
}
// ---------------------- OR----------------------
// Traits can be used during instantiation
class Derive extends BaseClass {
}
val classInst = new Derive with testTrait
Traits can also be chained using multiple with trait

Scala traits mixin order and super call

I have this code:
trait base{
def msg: Unit= {
println{"base"}
}
}
trait foo extends base {
abstract override def msg: Unit ={
super.msg
println("foo")
}
}
class base2{
def msg:Unit = {
println{"base 2"}
}
}
class test extends base2 with foo{
override def msg: Unit ={
super.msg
println("done")
}
}
If I call (new test).msg, this prints out things like: base, foo, done
However, if I change the base trait to:
trait base{
def msg: Unit
}
it prints out things like: base 2, foo, done
I understand the order of with is from right to left (last one comes first) but how about extends? How come sometimes it prints base2, but sometimes base?
When you omit the implementation, base is a template of a trait and has different evaluation rules. See the Scala specification
Scala has something called type linearization. It defines initialization order. Read here http://eed3si9n.com/constraining-class-linearization-in-Scala

What is the correct way to implement trait with generics in Scala?

I have some simple traits (Entity in the example below) that are extended by case classes in my app. I would like to create an EntityMapper trait that provides an interface for handling the case classes that extend the Entity trait (Foo in the example below). I thought I should be able to do this fairly easily using generics and bounding but I've spent a couple of hours on it already and I haven't gotten it to work correctly. The code below is what I think I should be able to do but it fails with a compiler error. The error is
Test.scala:15: error: value id is not a member of type parameter Foo \
println(e.id)
package experiment
trait Entity {
val id: Option[Long]
}
case class Foo(val id: Option[Long] = None) extends Entity
trait EntityMapper {
def create[E <: Entity](e: E): E
}
object FooMapper extends EntityMapper {
def create[Foo](e: Foo): Foo = {
println(e.id)
e
}
}
object Main extends App {
val foo = FooMapper.create(Foo(None))
}
I've tried several different things to solve the problem but nothing has worked. If I comment out the line in question "println(e.id)", it compiles but that is not useful because I cannot access or modify any of the properties of Foo.
I have tried using a covariant argument to the mapper trait and then supplying the type to the FooMapper object definition but that yields the same error. The code for that attempt is below:
trait EntityMapper[+Entity] {
def create[E <: Entity](e: E): E
}
object FooMapper extends EntityMapper[Foo] {
...
}
I have also tried achieving the same thing with simple inheritance but I cannot correctly restrict the type parameter in FooMapper to only take Foos, I have to make the method signature match the trait exactly which is why I started trying to implement it using generics with a type bound. The code for that attempt is below:
trait EntityMapper {
def create(e: Entity): Entity
}
object FooMapper extends EntityMapper {
def create(e: Foo): Foo = {
println(e.id)
e
}
}
The error code returned is:
Test.scala:13: error: object creation impossible, since method create in trait EntityMapper of type (e: experiment.Entity)experiment.Entity is not defined
(Note that experiment.Entity does not match experiment.Foo: class Foo in package experiment is a subclass of trait Entity in package experiment, but method parameter types must match exactly.)
object FooMapper extends EntityMapper {
^
Any help would be greatly appreciated. I'm using Scala version 2.10.3.
You can fix the error in a couple of ways
1.Specifying the generic type constraint on the trait.
trait EntityMapper[E <: Entity] {
def create(e: E): E
}
object FooMapper extends EntityMapper[Foo] {
def create(e: Foo): Foo = {
println(e.id)
e
}
}
2.Use parameterized types
trait EntityMapper {
type E <: Entity
def create(e: E): E
}
object FooMapper extends EntityMapper {
type E = Foo
def create(e: Foo): Foo = {
println(e.id)
e
}
}
Look at Scala: Abstract types vs generics to get some more background on the two approaches.

In Scala how to check the runtime type of an object in base trait

I have a set of classes that filter objects of only a particular type.
trait FilterTrait {
type RequiredType <: BaseType
def filter(baseObjects: Seq[BaseType]): Seq[BaseType] = {
val (requiredTypeObjects, nonRequiredTypeObjects) = baseObjects.partition(isOfRequiredType)
nonRequiredTypeObjects ++
filterRequiredType(requiredTypeObjects.asInstanceOf[Seq[RequiredType]])
}
def filterRequiredType(typeObjects: Seq[RequiredType]): Seq[RequiredType]
def isOfRequiredType[A <: BaseType](aObj: A): Boolean = ???(to be implemented)
}
class AFilter extends FilterTrait {
type RequiredType = CompoundType
...
}
class BFilter extends FilterTrait {
type RequiredType = BaseType with ATrait
...
}
I tried implementing isOfRequiredType method in the base class:
def isOfRequiredType[A <: BaseType](aObj: A): Boolean =
classOf[RequiredType].isAssignableFrom(aObj.getClass)
I get the error "class type required but RequiredType found".
I can get this working by implementing isOfRequiredType in all the sub classes. But I was wondering whether there is a way to get this to work by implementing isOfRequiredType in the base trait.
It can be easier done if you can use abstract class, like this:
abstract class Filter[RequiredType<:BaseType:Manifest] {
def isOfRequiredType[A <: BaseType](aObj: A): Boolean =
manifest[RequiredType].runtimeClass.isAssignableFrom(aObj.getClass)
...
}
The code above uses manifests - which are supposed to be replaced with TypeTags in the future, howeve I don't have to much experience with this part of scala 2.10 so I stick with manifest version for now.
https://groups.google.com/forum/#!msg/scala-user/2X7pHwqd6_A/fOd1yyURIhEJ
- here is discussion why it's not so easy with traits.