Garipaso
Garipaso

Reputation: 421

returning different return types in scala

Is there a way to return different return types from a single method in scala?

For example, if I have a load() method, I would like to return different data types depending on the object that called this method.

def load(path: String):<return type> 
{
    // if this instance is of type "type1", do some processing on this object, 
    // and return object of type "type1"

    // else if this instance is of type "type2", do some processing on this object,
    // and return object of type return "type2"
 }

Upvotes: 0

Views: 3773

Answers (5)

Raffaello
Raffaello

Reputation: 1706

what about factory method and just defining a trait loadable eg:

trait Loadable {
  def load(path: String): Loadable
}

class Type1 extends Loadable {
    def load(path: String): Type1 = this
}

class Type2 extends Loadable {
  def load(path: String): Type2 = this
}
object Main {
  def test(): Loadable = {
    new Type1().load("path")
  }

  def main(args: Array[String]): Unit = { 
    println(test().getClass)
  }
}

Upvotes: 0

sarveshseri
sarveshseri

Reputation: 13985

If your requirement is as simple as returning some instance of the type of context instance... ie this then you can just do this,

class A() {
  def omg(s: String): this.type = new A()
}

And if inheritence is involved,

trait A {
  type omgType

  def omg(s: String): omgType
}

class B() extends A {
  override type omgType = this.type

  override def omg(s: String): omgType = new B()
}

class C() extends A {
  override type omgType = this.type

  override def omg(s: String): omgType = new C()
}

But if you want more generality then you may want to read the following and apply it there,

The easiest way will be to take inspiration from the magnet pattern which was heavily used in Spray.

We can leverage the inspiration to build our custom solution, remember it is neither pure magnet pattern nor is path dependent type approach. Its a hacky cocktail of both.

So... lets say you want your def process to be able to support input parameters of type Int and String and finally return the respective result.

You will need to define implicit magnets for these types,

trait ProcessMagnet {
  type Input
  type Result
  def input: Input
  def process: Result
}

object ProcessMagnetProvider {
  implicit def stringToStringProcessMagnet(string: String): ProcessMagnet = new ProcessMagnet {
    override type Input = String
    override type Result = String

    override def input: Input = string
    // define this for your doing...
    override def process: Result = input + "_omg"
  }
  //... add for all your inputs
  implicit def intToIntProcessMagnet(int: Int): ProcessMagnet = new ProcessMagnet {
    override type Input = Int
    override type Result = Int

    override def input: Input = int
    // define this for your doing...
    override def process: Result = input + 1
  }
}

def process[T](t: T)(implicit pmConverter: T => ProcessMagnet): ProcessMagnet = pmConverter(t)

// now just import our implicit magnets...
import ProcessMagnetProvider._

val intResult: Int = process(5).process.asInstanceOf[Int]

val stringResult: String = process("omg").process.asInstanceOf[String]

Upvotes: 0

Kolmar
Kolmar

Reputation: 14224

If I understand correctly what you want, F-bounded polymorphism may work for you:

trait Base[T <: Base[T]] {
  def load(path: String): T
}

class Type1 extends Base[Type1] {
  override def load(path: String): Type1 = new Type1 // provisional implementation
}

class Type2 extends Base[Type2] {
  override def load(path: String): Type2 = new Type2
}

Then load will return the type of the current object. Note the result types in those expressions:

new Type1().load(path)
scala> res2: Type1 = Type1@744f0e0b

new Type2().load(path)
scala> res3: Type2 = Type2@77988c45

Upvotes: 4

pedrorijo91
pedrorijo91

Reputation: 7845

As others stated, you can use

You can use scala's Either

just keep in mind that every method that invokes your method, will need to check which of the types it is returning (using .map or pattern matching). Either is usually used like Either[ErrorType, NormalType] btw, but of course you can use it however you want

scala cats library has other alternative: http://eed3si9n.com/herding-cats/Xor.html

and of course, scalaz also provides an alternative: http://appliedscala.com/blog/2016/scalaz-disjunctions/

As a last resort, don't you can define your own "Either"

Upvotes: 0

You can use scala's Either:

def load(path : String) : Either[Type1, Type2] = {
  this match {
    case t1 : Type1 => Left(someProcessing(t1))
    case t2 : Type2 => Right(someOtherProcessing(t2))
  }
}

Upvotes: 2

Related Questions