Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Expressing a F bounded type as abstract type member [duplicate]

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?

Edit 1

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?

Edit 2:

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.

Edit 3:

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?

like image 347
arturaz Avatar asked Feb 02 '15 18:02

arturaz


1 Answers

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.

like image 71
Jatin Avatar answered Oct 20 '22 16:10

Jatin