I want to convert F bounded polymorphism to abstract type members.
trait FBoundedMovable[Self <: FBoundedMovable[Self]] {
def moveTo(pos: Vect2): Self
}
to
trait Movable { self =>
type Self <: (Movable { type Self = self.Self })
def moveTo(pos: Vect2): Self
}
So far so good.
Lets define an instance:
case class Ship(pos: Vect2) extends Movable {
type Self = Ship
def moveTo(pos: Vect2) = copy(pos = pos)
}
And try to use it:
// [error] found : a.Self
// [error] required: A
def move[A <: Movable](a: A, to: Vect2): A = a.moveTo(to)
F bounded version works fine.
def moveF[A <: FBoundedMovable[A]](a: A, to: Vect2): A = a.moveTo(to)
I know it's possible to add type bounds to method definition site:
def move2[A <: Movable { type Self = A }](a: A, to: Vect2): A = a.moveTo(to)
But is it possible to specify the relationship in Movable trait declaration? If not - why?
I've realised what problem I am having.
Lets say we want to declare that something is a unit in our world.
trait WorldUnit extends Movable with Damageable
All units are movable and damagable.
Our combat calculation stuff only cares that stuff is Movable with Damagable. It doesn't care whether it is unit or building.
However we can have code like this:
def doCombat(obj: Movable with Damagable) = obj.moveTo(...).takeDamage(...)
def doStuffWithUnit(obj: WorldUnit): WorldUnit = doCombat(obj) // and the type is lost here.
Am I doomed for F bounded types?
The question is not answered by Attempting to model F-bounded polymorphism as a type member in Scala - I've tried that before and it doesn't impact the return type in a slightest, it's still a.Self.
I've found http://blog.jessitron.com/2014/02/when-oo-and-fp-meet-mytype-problem.html but a problem is still unresolved.
Basically, whenever you have a collection and want to pick one:
(collection: Seq[Movable]).collectFirst { m: Movable if m.someCondition => m } - you have no way of specifying the type bound, thus compiler cannot prove that A#Self =:= A?
Type-Projections in scala are path-dependent. Quick example
scala> trait A{
| type T
| }
defined trait A
scala> val a = new A{type T = String}
a: A{type T = String} = $anon$1@31198ceb
scala> val b = new A{type T = String}
b: A{type T = String} = $anon$1@236ab296
scala> def f(implicit evidence: A =:= b.T) = null
f: (implicit evidence: =:=[A,b.T])Null
scala> f("asdf":a.T)
<console>:12: error: type mismatch;
found : a.T
(which expands to) String
required: =:=[A,b.T]
(which expands to) =:=[A,String]
f("asdf":a.T)
^
In your case, it throws an error because of the return type. It rightly expects a.type but you return A. And they are not the same.
The reason why they shouldn't be the same is:
a.type returns a type <: Movable. For imagination, some number x less than 100. Method move returns A and for imagination, it is some another number y less than 100. Its not necessary that x should be same as y.
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