why trait method needs asInstanceOf and class method don't - scala

I have defined the following trait:
trait Felem[T <: Felem[T]] {
def mul(that: T): T
def square: T = this.mul(this.asInstanceOf[T])
}
I also define a class based on this trait:
class F2elem(val coef: Boolean) extends Felem[F2elem] {
override def square: F2elem = this.mul(this)
...
}
My questions are about the need of "asInstanceOf" in the definition of the "square" method in the trait. If I remove it, I get the following error:
error: type mismatch;
found : Felem.this.type (with underlying type Felem[T])
required: T
def square: T = this.mul(this)
Why is it needed in the trait ?
Why it is not needed in the class ?
Does it cost anything in term of execution time or memory ?

The parameter of mult must be of type T.
When calling mul(this), the this parameter is of type Felem[T], which is not and does not conform to T. There is the additional constraint that T conforms to Felem[T]. But this is not what you want, you would need the opposite, Felem[T] to conform to T.
On the other hand, in F2elem, T is exactly F2elem, so it typechecks (completley unrelated to one being a trait and the other one a class)
Here is example to show that the definition in Felem must indeed not typecheck, and that it is possible to have implementors where Felem[T] does not conform to T.
class F3elem extends Felem[F2elem] // this is 2, not 3
This declaration is correct, F2elem which is given for T satisfies T <: Felem[T].
However, an inherited this.mul(this) in square would be invalid, mult expect a T, that is F2elem, and this is F3elem. And they are unrelated.
What you probably want is that every Felem must be like F2elem, that is that T must be the type of the actual class. You can enforce this with a self type.
trait Felem [T <: Felem[T]] { this: T => /* your code */ }
When you write that, you state that in every implementation, the type of the implementation must conform to T. Doing that, it will typecheck, and you will not be allowed to instanciate F3elem above :
error: illegal inheritance; self-type F3elem does not conform to
Felem[F2elem]'s selftype F2elem
class F3elem extends Felem[F2elem] {

1) In your trait this is not an instance of T:
scala> trait Felem[T <: Felem[T]] {
| def mul(that: T): T = that
| def square: T = this.mul(this.asInstanceOf[T])
| }
defined trait Felem
scala> class F2elem extends Felem[F2elem]
defined class F2elem
scala> class F3elem extends Felem[F2elem]
defined class F3elem
scala> new F3elem()
res1: F3elem = F3elem#2e0b08f1
scala> res1.square
java.lang.ClassCastException: F3elem cannot be cast to F2elem
2) In your class this is F2elem and T == F2elem, so this is T.

You're using T as your method's parameter type. This means that whatever the type of T is will be the type that's required in the method (hence not needing the cast in the class, since its type is what is signified by T originally).
If you change the type of that to Felem[T], you will not need the cast.

Related

Why is it possible to define a class with a unreified path-dependent type?

Considering the following example:
trait Supe {
type Out <: Supe
def out: Out
}
class Reif1 extends Supe {
type Out = Reif1
override def out: Out = this
}
class Reif2 extends Supe {
type Out >: this.type <: Reif2
override def out: Out = this
}
class Reif1 should obviously work. as type Out is reified and becomes a type alias
class Reif2 also works but seriously? type Out only has upper/lower bound defined and the bound was not even tight enough: this.type is a singleton type, and Reif2 is a class type. So what exactly would Out look like if Reif2 got instantiated? Is it going to be this.type? Or Reif2? But the bigger question should be: why scalac 2.12/2.13 allows it be compiled?
In Scala (or in DOT calculus 1 2) all types are intervals.
type Out = Reif1 is (or is supposed to be) type Out >: Reif1 <: Reif1.
Abstract type without bounds type Out is type Out >: Nothing <: Any.
So what exactly would Out look like if Reif2 got instantiated?
It will remain exactly type Out >: this.type <: Reif2
val r = new Reif2
import scala.reflect.runtime.universe._
typeOf[r.Out] // App.r.Out
showRaw(typeOf[r.Out]) // TypeRef(SingleType(ThisType(App), TermName("r")), TypeName("Out"), List())
typeOf[r.Out].typeSymbol.isAbstract // true
typeOf[r.Out].typeSymbol.typeSignature // >: Reif2.this.type <: App.Reif2
If you replace type Out >: this.type <: Reif2 in Reif2 with type Out = this.type (or type Out = Reif2 or type Out = Supe) then isAbstract will return false.
Abstract type member of a singleton object (see application of abstract type)
What is the meaning of a type declaration without definition in an object? (see why it's not easy to check that type T >: L <: U is not abstract)
Concrete classes can have abstract type members #1753
SI-8217 allow abstract type members in objects #4024
Abstract type members are incorrectly forbidden in objects (unless inherited) #8217
Use of abstract type in a concrete class?
Concrete classes with abstract type members
What's different between "def apply[T](c:T)" and "type T;def apply(c:T)"

scala: overriding a value of generic, existential type with a concretised type

I have a generic trait MappingPath, invariant regarding it's type parameters:
trait MappingPath[X<:AnyMapping, Y<:AnyMapping]
and an interface of a factory for it:
trait Pathfinder[X, Y] {
def apply(fun :X=>Y) :MappingPath[_<:AnyMapping,_<:AnyMapping]
def get(fun :X=>Y) :Option[MappingPath[_<:AnyMapping, _<:AnyMapping]]
}
I start a skeleton implementation which works for a single mapping:
class MappingPathfinder[M<:AnyMapping, X, Y] extends Pathfinder[X, Y] {
override def apply(fun :X=>Y) :MappingPath[M, _<:AnyMapping] = ???
override def get(fun :X=>Y) :Option[MappingPath[M, _<:AnyMapping]] = ???
}
which produces a compile error complaining that MappingPathfinder.apply overrides nothing and doesn't implement Pathfinder.apply. What's interesting, replacing M with _<:AnyMapping in apply's return type makes it compile, and no complaints are made regarding similar get method.
What's going on? I use scala 2.11.5.
EDIT:
I was able to circumvene my problem by adding explicit existantial annotations:
//Pathfinder
def apply(fun :X=>Y) :MappingPath[A, B] forSome { type A<:AnyMapping; type B<:AnyMapping }
//MappingPathfinder
def apply(fun :X=>Y) :MappingPath[A, B] forSome { type A>:M<:M; type B<:AnyMapping }
It seems to work, i.e
I can do:
(p :MappingPath[_<:AnyMapping, M]) ++ mappingPathfinder(f),
where ++ requires a path starting with the exact same type as this ends. It looks a bit silly and certainly confusing though.
Not an answer, but your use case can be simplified to:
trait Higher[U]
trait Super {
def foo: Higher[_]
}
trait Sub[M] {
override def foo: Higher[M] // error: method foo overrides nothing
}
Instead of existential types, I would use a type member:
trait Super {
type U
def foo: Higher[U]
}
trait Sub[M] {
type U = M
}
I think the difference is that in the case of the existential type, you only specify that the type parameter returned has some upper bound, but not necessarily that it is always the same type; whereas in my second example, type U means this will eventually be one specific type, and you can only refine a specific type. You can make upper bounds more precise:
trait Upper
trait A {
type U <: Upper
}
trait Specific extends Upper
trait B extends A {
type U <: Specific // type is "overridden"
}
If it's possible, I would avoid existential types, and your case seems a perfect fit for such avoidance. Most of the time, existential types are only needed for Java interop.

Scala multiple type conformance

I have the following code and will like to call a method on top of a class that implements the trait EventTraces[T] and at the same time T should implement the trait Event (e.g as in doSomethingOnTopOfAnEventTrace())
trait Event
class ConcreteEvent[T <: Event]
trait EventTrace[T <: Event]
class ConcreteEventTrace[T <: Event] extends EventTrace[T]
val concreteEventTrace : ConcreteEventTrace[ConcreteEvent] = new ConcreteEventTrace(new ConcreteEvent)
def doSomethingOnTopOfAnEventTrace[T <: Event, Z <: EventTrace[T]](eventTrace: Z) {
println("Action on top of a Any kind of EventTrace of any type of Event")
}
However calling doSomethingOnTopOfAnEventTrace(concreteEventTrace) gives me the following error:
Error:(129, 3) inferred type arguments [Nothing,ConcreteEventTrace[ConcreteEvent]] do not conform to method doSomethingOnTopOfAnEventTrace type parameter bounds [T <: Event,Z <: EventTrace[T]]
doSomethingOnTopOfAnEventTrace(concreteEventTrace)
^
Error:(129, 38) type mismatch;
found : ConcreteEventTrace[ConcreteEvent]
required: Z
doSomethingOnTopOfAnEventTrace(concreteEventTrace)
^
The issue is that Scala can't infer T here because it only has Z in the arguments (I don't see a reason it couldn't, in this case). The obvious way to fix it is doSomethingOnTopOfAnEventTrace[ConcreteEvent, ConcreteEventTrace[ConcreteEvent]](concreteEventTrace). Another, if you don't actually need to be generic in Z:
def doSomethingOnTopOfAnEventTrace[T <: Event](eventTrace: EventTrace[T])
You could also try to use type members instead of generics in Event and EventTrace.

Scala: Extract types from generic parameters

I have a class like this:
abstract class Foo[I, T, A <: Bar[I, T]](x: SomeClass[A]){
When I want to inherit class Foo, I've to specify types T and I, which could be extracted from type parameters of type A. (I.e. there are enough data to extract these types.) Does the Scala compiler allow to extract them somehow? I'd like to write something like:
abstract class Foo[A <: Bar[_, _]](x: SomeClass[A]){
type Bar[I, T] = A // <-- something like pattern matching
It is strange that I can write that, but the type Bar[I, T] = A line does not seem to declare anything. The line passes, but I can use neither type I nor type T.
Can I do something similar?
I know I could use abstract class Foo[I, T](x: SomeClass[A]){ and then define type A = Bar[I, T], but it loses some universality. Additionaly, this case means more (boilerplate) code for the code users, because they are likely to define a shortcut (i.e. type alias) for Bar[I, T].
I can rewrite the abstract class Foo to a trait and I probably will do so. But I am not sure if it could help.
When type parameters are going to be unhandy one should give abstract types a try:
scala> class Bar[I, T](i: I, t: T)
defined class Bar
scala> class SomeClass[A](a: A)
defined class SomeClass
scala> trait Trait { type I; type T; type A = Bar[I,T] }
defined trait Trait
scala> class Foo(x: SomeClass[Foo#A]) extends Trait { type I = String; type T = String }
defined class Foo
scala> new Foo(new SomeClass(new Bar("", ""))) // works
res0: Foo = Foo#7e1f613c
scala> new Foo(new SomeClass(new Bar("", 0))) // does not work
<console>:12: error: type mismatch;
found : Int(0)
required: String
new Foo(new SomeClass(new Bar("", 0)))
^

Type aliasing type constraints in generics

I have a situation where I want to use a bounded generic type as a constraint on what class can be produced. The problem is that I need to
abstract class SomeAbstractClass
trait Foo[A <: SomeAbstractClass]
trait Bar[A] extends Foo[A]
// Fails error: type arguments [A] do not conform to trait Foo's type parameter bounds [A <: SomeAbstractClass]
// Need to write it like this, for every single subclass of Foo
trait Bar[A <: SomeAbstractClass] extends Foo[A]
Is there an easier way to promote that through the system without having to retype the bounds every time?
Constraints on type parameters are constraints. They don't propagate transitively via inheritance as you would like them to.
Perhaps this is applicable or produces some new ideas at least:
abstract class SomeAbstractClass
trait Foo { // propagated by abstract type member
type A <: SomeAbstractClass
}
trait Bar extends Foo // no generic type parameter needed here
trait BAR[SAC <: SomeAbstractClass] extends Bar { type A = SAC } // introduce type parameter
trait Baz[SAC <: SomeAbstractClass] extends BAR[SAC] // generic type parameter needed here