John Threepwood
John Threepwood

Reputation: 16143

Is it possible to write a method in Scala returning objects with different type parameter?

Is it possible to write a method in Scala which returns an object of a type-parameterized class with different type paramter ? Something like this:

class A[T]

def f(switch: Boolean): A = if(switch) new A[Int] else new A[String]

Please note: The Code above is fictional to show the type of problem; The code above does not make semantically sense.

The code above will not compile because return type A is not parameterized.

Upvotes: 3

Views: 2625

Answers (4)

Oleg Mirzov
Oleg Mirzov

Reputation: 155

How about an absolutely minimal change to the "fictional code"? If we just add [_] after the "fictional" return type, the code will compile:

class A[T]
def f(switch: Boolean):A[_] = if(switch) new A[Int] else new A[String]

It is worth noting that A[_] is not the same as A[Any]. A[T] does not need to be defined covariant for the code to compile. Unfortunately, information about the type gets lost.

Upvotes: -1

oxbow_lakes
oxbow_lakes

Reputation: 134270

One way of expressing this would be by using Either;

def f(switch: Boolean) = if (switch) Left(new A[Int]) else Right(newA[String])

This of course returns an Either[A[Int], A[String]]. You certainly cannot (at the moment) declare a method which returns some parameterized type P, with some subset of type parameters (i.e. only Int or String).

The language ceylon has union types and I understand the intention is to add these to scala in the near future, in which case, you could define a method:

def f(switch: Boolean): A[Int|String] = ...

Upvotes: 4

missingfaktor
missingfaktor

Reputation: 92026

Well, you could do something like that.

scala> class A {
     |   type T
     | }
defined class A

scala> def f(b: Boolean): A = if(b) new A { type T = Int } else new A { type T = String }
f: (b: Boolean)A

But this is pointless. Types are a compile time information, and that information is getting lost here.

Upvotes: 0

Rex Kerr
Rex Kerr

Reputation: 167871

You can, and you can even do it with type-safety with the aid of implicit arguments that encapsulate the pairings:

class TypeMapping[+A,B] {
  def newListB = List.empty[B]
}
trait Logical
object True extends Logical
object False extends Logical

implicit val mapFalseToInt = new TypeMapping[False.type,Int]
implicit val mapTrueToString = new TypeMapping[True.type,String]

def f[A <: Logical,B](switch: A)(implicit tmap: TypeMapping[A,B]) = tmap.newListB

scala> f(True)
res2: List[String] = List()

scala> f(False)
res3: List[Int] = List()

You do have to explicitly map from boolean values to the custom True and False values.

(I have chosen List as the target class just as an example; you could pick anything or even make it generic with a little more work.)

(Edit: as oxbow_lakes points out, if you need all possible return values to be represented on the same code path, then this alone won't do it, because the superclass of List[Int] and List[String] is List[Any], which isn't much help. In that case, you should use an Either. My solution is for a single function that will be used only in the True or False contexts, and can maintain the type information there.)

Upvotes: 6

Related Questions