2

I tried to generalize a method to provide a type safe API as follows:

abstract class AbstractCommand {
  type T = this.type
  def shuffler(s: T => Seq[AbstractCommand])
}

class TestCommand extends AbstractCommand {
  override def shuffler(s: (TestCommand) => Seq[AbstractCommand]): Unit = ??? //error
}

I wanted the expected type of the function argument to be the most specific in this hierarchy. But it didn't work.

Is there a way to do something like that in Scala without introducing some helper type parameters?

stefanobaghino
  • 11,253
  • 4
  • 35
  • 63
St.Antario
  • 26,175
  • 41
  • 130
  • 318

3 Answers3

4

This looks like a perfect use-case for F-Bounded Polymorphism:

abstract class AbstractCommand[T <: AbstractCommand[T]] {
  self: T =>
  def shuffler(s: T => Seq[AbstractCommand[T]])
}

class TestCommand extends AbstractCommand[TestCommand] {
  override def shuffler(s: (TestCommand) => Seq[AbstractCommand[TestCommand]]): Unit = ???
}

And with a type member instead of a type parameter (using the example provided by Attempting to model F-bounded polymorphism as a type member in Scala):

abstract class AbstractCommand { self =>
  type T >: self.type <: AbstractCommand
}

class TestCommand extends AbstractCommand {
  type T = TestCommand
}

class OtherCommand extends AbstractCommand {
  type T = OtherCommand
}
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
2

You could avoid defining T in the abstract class :

abstract class AbstractCommand {
  type T
  def shuffler(s: T => Seq[AbstractCommand])
}

class TestCommand extends AbstractCommand {
  type T = TestCommand
  override def shuffler(s: (TestCommand) => Seq[AbstractCommand]): Unit = ??? //compiles !
}

On the downside, it's a bit more verbose, on the upside, it's even more generic !

C4stor
  • 8,355
  • 6
  • 29
  • 47
2

I'm not entirely sure it fits your need, but I've been able to compile and run the following, let me know if it helps:

abstract class AbstractCommand {
  def shuffler(s: this.type => Seq[AbstractCommand])
}

class TestCommand extends AbstractCommand {
  override def shuffler(s: (TestCommand.this.type) => Seq[AbstractCommand]): Unit = {
    s(this)
    println("success")
  }
}

new TestCommand().shuffler(_ => Seq.empty) // prints "success"
stefanobaghino
  • 11,253
  • 4
  • 35
  • 63
  • The thing is I want the only most specific type to be accepted – St.Antario Jul 06 '17 at 10:38
  • From what I can get, `TestCommand` is the most specific type (as it's a specialization of `AbstractCommand`) and only that gets accepted for that particular implementation; can you perhaps show me an example to clarify your remark? Maybe use a gist or something like that. – stefanobaghino Jul 06 '17 at 10:43
  • Actually, yes. F-bound polymorphism is a solution – St.Antario Jul 06 '17 at 10:55
  • Ok, but just to come clear, this is not F-Bounded Polymorphism, which requires a type parameter. You can find more about it here: https://twitter.github.io/scala_school/advanced-types.html#fbounded – stefanobaghino Jul 06 '17 at 10:57
  • @stefanobaghino I am not sure if you know and simply don't mention this in the answer, but `this.type` here is a lot more specific than `TestCommand`. If you try the signature author wants (`def shuffler(s: TestCommand => Seq[AbstractCommand]): Unit`) it won't compile, unless I miscounted variances. – Alexey Romanov Jul 06 '17 at 19:56