Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

scala generic function return type

I tried writing a function with a generic return type but it doesn't work unless I cast the return type. Please see the function getSomething() below I expected it to work without the casting. What might I be doing wrong here?

trait Sup

class Sub extends Sup {
  def getString = "I am Sub"
}

class Sub2 extends Sup {
  def getInt = 100
}

def getSomething[A <: Sup](str: String) : A  = {
  str match {
    case "sub" => getSub.asInstanceOf[A]
    case "sub2" => getSub2.asInstanceOf[A]
  }
}

def getSub(): Sub = {
  new Sub
}

def getSub2() : Sub2 = {
  new Sub2
}

val x = getSomething[Sub]("sub").getString
val y = getSomething[Sub2]("sub2").getInt
like image 272
inglorious_engineer Avatar asked Nov 05 '16 17:11

inglorious_engineer


People also ask

What is generic function in Scala?

The classes that takes a type just like a parameter are known to be Generic Classes in Scala. This classes takes a type like a parameter inside the square brackets i.e, [ ]. This classes are utilized explicitly for the progress of the collection classes in Scala.

How do I use generic in Scala?

To use a generic class, put the type in the square brackets in place of A . The instance stack can only take Ints. However, if the type argument had subtypes, those could be passed in: Scala 2.

What are the delimiters that you use in Scala to specify type parameters?

Methods in Scala can be parameterized by type as well as by value. The syntax is similar to that of generic classes. Type parameters are enclosed in square brackets, while value parameters are enclosed in parentheses.


2 Answers

As Alexey mentions, the instanceOf is needed to force a link between the expected type and the type of the object returned. It's the equivalent of saying: "Compiler, trust me, I'm giving you an 'A'" and it's not very safe as it depends of us to provide a correct type.

If we want the type system to figure things out for us, we need to give it some additional information. One way to do that in Scala is to define some factory that knows how to produce instances of our types and evidence that allows that factory to return our specific types.

This is a version of the code above introducing such construct and using ContextBounds to obtain the right factory instance of the type we want.

trait Sup 

class Sub extends Sup {
  val str = "I'm a Sub"
}

class Sub2 extends Sup {
  val number = 42
}

trait SupProvider[T <: Sup] {
  def instance:T
}

object SupProvider {
  def getSomeSup[T<:Sup:SupProvider]: T = implicitly[SupProvider[T]].instance
  implicit object SubProvider extends SupProvider[Sub] {
    def instance = new Sub
  }
  implicit object Sub2Provider extends SupProvider[Sub2] {
    def instance = new Sub2
  }  
}

SupProvider.getSomeSup[Sub].str
// res: String = I'm a Sub

SupProvider.getSomeSup[Sub2].number
// res: Int = 42
like image 114
maasg Avatar answered Sep 25 '22 10:09

maasg


The error messages you get without asInstanceOf tell you exactly what you are doing wrong. In case "sub", the body gives a Sub, and the compiler has no reason to think A is a supertype of Sub (or that Sub can be implicitly converted to A).

Suppose it worked. Then the following calls would be legal:

val z = getSomething[Sub]("sub2").getString

or

trait Sub3 extends Sup
val w = getSomething[Sub3]("sup")

What should happen in either case?

like image 42
Alexey Romanov Avatar answered Sep 26 '22 10:09

Alexey Romanov