accept multiple types for a parameter in scala
Asked Answered
I

2

20

I have two objects, ObjectA and ObjectB, both with a method update(). I want to write a function that accepts either ObjectA or ObjectB (but no other types). Conceptually, this is what I am trying to do:

def doSomething[T <: ObjectA | T <: ObjectB](obj: T) = {
    obj.update
}

I realize there are other ways to solve this problem (eg, structural typing of the update() method, common base class, etc) but my question is it is possible to do it this way in Scala and if so what is the syntax? And what is this called?

Illailladvised answered 7/3, 2012 at 23:54 Comment(4)
How would you expect this to work? How would the compiler know what methods T had if it didn't know what type it was? If you mean to make sure your class has an update, then that's what structural types are for.Crack
possible duplicate of Does Scala have "type disjunction" (union types)?. Both the accepted answer and the next most highly ranked referring my unboxed union encoding directly resolve this question.Biotin
@dhg, if both ObjectA and ObjectB have a method update() and type T extends one or the other of those classes, it could know that type T has an update() method.Illailladvised
@Miles Sabin, thank you for that link with the additional info which also answers my question about what these are called.Illailladvised
H
21

In Scala, there is the type Either to make a disjoint union. Basically, you will do something like:

def doSomething(obj: Either[ObjectA, ObjectB]) {
  obj.fold(fa, fb)
}

Checkout http://www.scala-lang.org/api/current/scala/Either.html

Holiness answered 8/3, 2012 at 0:12 Comment(5)
If you want to treat them as one, you could furthermore use a structural type: obj.fold[{ def update : Unit }]( identity, identity ).updateFragile
I have no idea where fa or fb came from in your example or why you are calling fold() on obj, but Either seems to be the closest, cleanest way to do this. Thanks.Illailladvised
@Bigwheels it would be interseting to know the context of your problem because i may be wrong but it doesn't really make sens to apply the same function to the two possible objects of a union. Basically, fa and fb are closures that you aplly on an ObjectA or an ObjectB. I didn't define them in my example. You should check Miles Sabin comment on your question about unions without Either, it's pretty technical type hacking however.Holiness
The reason that I asked about this is because I am using a library where two objects are identical in nearly every way (except for name) and in some places they use the one object and in other places the other object.Illailladvised
seems to be the most cleanest way to do it .. you can also wrap into a class if the two objects or impn can be abstractedOnomatology
R
1

As mentioned in accepted answer, Either is perfectly suitable for your problem

Here's a solution when field members are not the same

def doSomething(obj: Either[ClassA, ClassB]): String = {
  obj match {
    case Left(objA) => objA.field1
    case Right(objB) => objB.field2
  }
}

doSomething(Left(new ClassA(field1="something")))

doSomething(Right(new ClassB(field2="somethingelse")))

Here's a solution when we have to accept multiple Types

def doSomething[T](obj: T): String = {
  obj match {
    case objA: ClassA => objA.field1
    case objB: ClassB => objB.field2
    case objC: ClassC => objC.field3
  }
}

doSomething(new ClassA(field1="something"))

doSomething(new ClassB(field2="somethingelse"))

doSomething(new ClassC(field3="another"))
Rind answered 13/12, 2022 at 22:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.