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