Of course I realize all types do have a common ancestor, but what I mean is this:
In dynamically-typed languages, it is a common practice to have ‘mixed’ return types. A common case is a function which attempts to retrieve data from a database, then returns either an object (initialized with the found data) or FALSE (in the event no data was found).
A little pseudocode to demonstrate just such an anti-pattern:
function getObjectFromDatabase(object_id) {
if(result = db_fetch_object("SELECT * FROM objects WHERE id = %d", object_id) {
return result
} else {
return FALSE
}
}
If data is found for my object id, I get a DB record back as an object. If not, I get a boolean value. Then, of course, it is on me, the client, to handle multiple possible return types.
Is the only way to accomplish this in Scala to find a common ancestor for all possible return types and declare that as the return type in the signature?
// Like so:
def getObjectFromDatabase(objectId: Int): Any = {
val result = dbFetchObject("SELECT * FROM objects WHERE id = %d", object_id)
if(result) {
return result
} else {
return false
}
}
Or is it possible to annotate multiple possible return types?
(Note that I do not hope it is possible to do this, as I would prefer it to be enforced that function return types are as unambiguous as possible. It would come as a relief to me to learn that the language forbids ambiguous return types, which is more the reason I am asking.)
What you are looking for is called a tagged union, variant, variant record, discriminated union, disjoint union, or sum type.
Combined with product types, they become algebraic datatypes.
Scala does not have direct support for algebraic datatypes, but it doesn’t need to, because they can be easily modeled by inheritance. (Scala does have the
sealedmodifier to support closed ADTs.)NOTE: As of Scala 3, Scala does support algebraic sum types directly, in the form of
enums. All of the below only applies to Scala 2.In your example, if you know that the return type is either
SomeTypeorSomeOtherType, you can model it like this:If you don’t know what the return types are, only that there are two of them, then you can model it similarly:
This is actually a well-known datatype called an
Either(because it holds either anAor aB), and is present in Scala’s standard library asscala.util.Either.But in your specific case, there is a more specific type, called
MaybeorOption, which encapsulates the idea that a value maybe exists or maybe not. It looks something like this:Again, this is already provided by Scala as
scala.Option.The advantage of
EitheroverOptionis that it allows you to also return information in the failure case instead of only indicating that there is no value you can also say why there is no value. (By convention, the left side of theEitheris the error, the right side is the "useful" value.)The advantage of
Optionis that it is a monad. (Note: you can makeEithera monad by biasing it either to the left or the right.)