Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Polymorphic Scala return type

I have an abstract Scala class Base which has subclasses Derived1 and Derived2. Base defines a function f() which returns an object of the same type as its implementing class. So Derived1.f() returns Derived1 and Derived2.f() returns Derived2. How do I write this in Scala?

Here is what I have come up with so far.

package com.github.wpm.cancan

abstract class Base {
  def f[C <: Base]: C
}

case class Derived1(x: Int) extends Base {
  def f[Derived1] = Derived1(x + 1)
}

case class Derived2(x: Int) extends Base {
  def f[Derived2] = Derived2(x + 2)
}

This gives the following compiler errors:

type mismatch;
[error]  found   : com.github.wpm.cancan.Derived1
[error]  required: Derived1
[error]   def f[Derived1] = Derived1(x + 1)

type mismatch;
[error]  found   : com.github.wpm.cancan.Derived2
[error]  required: Derived2
[error]   def f[Derived2] = Derived2(x + 2)

This error message is confusing to me because I think com.github.wpm.cancan.Derived1 should be the same as Derived1 in this context.

like image 864
W.P. McNeill Avatar asked Feb 06 '13 23:02

W.P. McNeill


People also ask

What is return type in polymorphism?

Edit: By return type polymorphism I mean overloading the function signature only in the return type. For example, C++ and Java only allow overloading in the type of the formal parameters, not in the return type.

What is a polymorphic function Scala?

Polymorphism is the ability of any data to be processed in more than one form. The word itself indicates the meaning as means many and. means types. Scala implements polymorphism through virtual functions, overloaded functions and overloaded operators.

What does => mean in Scala?

=> is syntactic sugar for creating instances of functions. Recall that every function in scala is an instance of a class. For example, the type Int => String , is equivalent to the type Function1[Int,String] i.e. a function that takes an argument of type Int and returns a String .

What are type parameters in Scala?

Methods in Scala can be parameterized by type as well as value. The syntax is similar to that of generic classes. Type parameters are enclosed in square brackets, while value parameters are enclosed in parentheses. The method listOfDuplicates takes a type parameter A and value parameters x and length .


2 Answers

Randall Schulz pointed out one of the reasons your current code doesn't work. It is possible to get what you want, though, with F-bounded polymorphism:

trait Base[C <: Base[C]] { def f: C }

case class Derived1(x: Int) extends Base[Derived1] {
  def f: Derived1 = Derived1(x + 1)
}

case class Derived2(x: Int) extends Base[Derived2] {
  // Note that you don't have to provide the return type here.
  def f = Derived2(x + 2)
}

The type parameter on the base trait allows you to talk about the implementing class there—e.g. in the return type for f.

like image 54
Travis Brown Avatar answered Oct 18 '22 04:10

Travis Brown


Just to add a small precision about (perfectly good) Travis Brown answer: It's not that C in trait Base[C <: Base[C]] let's you refer to the implementing class; it's just sticking to the convention of writing subclass extends Base[subclass] that let's you do so. There's no way that I know of to refer to this type. To clarify what I mean, this compiles

trait Base[C <: Base[C]] { def f: C }

case class Derived1(x: Int) extends Base[Derived1] {
  def f: Derived1 = Derived1(x + 1)
}
// a Derived2 where f returns Derived1!!
case class Derived2(x: Int) extends Base[Derived1] {
  def f = Derived1(x + 2)
}

Now, if all you're going to have as implementations of Base are case classes, you can get this right through a self-type bound:

trait Base[C <: Base[C]] { self: C => 
  def f: C 
}

case class Derived1(x: Int) extends Base[Derived1] {
  def f: Derived1 = Derived1(x + 1)
}
// a Derived2 where f returns Derived1!!
// this won't compile now
case class Derived2(x: Int) extends Base[Derived1] {
  def f = Derived1(x + 2)
}
like image 21
Eduardo Pareja Tobes Avatar answered Oct 18 '22 05:10

Eduardo Pareja Tobes