Converts this Or
to an Or
with the same Good
type and a Bad
type consisting of
One
parameterized by this Or
's Bad
type.
Converts this Or
to an Or
with the same Good
type and a Bad
type consisting of
One
parameterized by this Or
's Bad
type.
For example, invoking the accumulating
method on an Int Or ErrorMessage
would convert it to an
Int Or One[ErrorMessage]
. This result type, because the Bad
type is an Every
, can be used
with the mechanisms provided in trait Accumulation
to accumulate errors.
Note that if this Or
is already an accumulating Or
, the behavior of this accumulating
method does not change.
For example, if you invoke accumulating
on an Int Or One[ErrorMessage]
you will be rewarded with an
Int Or One[One[ErrorMessage]]
.
this Good
, if this Or
is a Good
; or this Bad
value wrapped in a One
if
this Or
is a Bad
.
Maps the given function to this Or
's value if it is a Bad
or returns this
if it is a Good
.
Maps the given function to this Or
's value if it is a Bad
or returns this
if it is a Good
.
the function to apply
if this is a Bad
, the result of applying the given function to the contained value wrapped in a Bad
,
else this Good
is returned
Returns true
if this Or
is a Good
and the predicate p
returns true when applied to this Good
's value.
Returns true
if this Or
is a Good
and the predicate p
returns true when applied to this Good
's value.
Note: The exists
method will return the same result as forall
if this Or
is a Good
, but the opposite
result if this Or
is a Bad
.
the predicate to apply to the Good
value, if this is a Good
the result of applying the passed predicate p
to the Good
value, if this is a Good
, else false
Returns this Or
if either 1) it is a Bad
or 2) it is a Good
and applying the validation function f
to this
Good
's value returns Pass
; otherwise,
returns a new Bad
containing the error value contained in the Fail
resulting from applying the validation
function f
to this Good
's value.
Returns this Or
if either 1) it is a Bad
or 2) it is a Good
and applying the validation function f
to this
Good
's value returns Pass
; otherwise,
returns a new Bad
containing the error value contained in the Fail
resulting from applying the validation
function f
to this Good
's value.
For examples of filter
used in for
expressions, see the main documentation for trait
Validation
.
the validation function to apply
a Good
if this Or
is a Good
that passes the validation function, else a Bad
.
Returns the given function applied to the value contained in this Or
if it is a Good
,
or returns this
if it is a Bad
.
Returns the given function applied to the value contained in this Or
if it is a Good
,
or returns this
if it is a Bad
.
the function to apply
if this is a Good
, the result of applying the given function to the contained value wrapped in a Good
,
else this Bad
is returned
Folds this Or
into a value of type V
by applying the given gf
function if this is
a Good
else the given bf
function if this is a Bad
.
Folds this Or
into a value of type V
by applying the given gf
function if this is
a Good
else the given bf
function if this is a Bad
.
the function to apply to this Or
's Good
value, if it is a Good
the function to apply to this Or
's Bad
value, if it is a Bad
the result of applying the appropriate one of the two passed functions, gf
or bf, to this Or
's value
Returns true
if either this Or
is a Bad
or if the predicate p
returns true
when applied
to this Good
's value.
Returns true
if either this Or
is a Bad
or if the predicate p
returns true
when applied
to this Good
's value.
Note: The forall
method will return the same result as exists
if this Or
is a Good
, but the opposite
result if this Or
is a Bad
.
the result of applying the passed predicate p
to the Good
value, if this is a Good
, else true
Applies the given function f to the contained value if this Or
is a Good
; does nothing if this Or
is a Bad
.
Applies the given function f to the contained value if this Or
is a Good
; does nothing if this Or
is a Bad
.
the function to apply
Returns the Or
's value if it is a Good
or throws NoSuchElementException
if it is a Bad
.
Returns the Or
's value if it is a Good
or throws NoSuchElementException
if it is a Bad
.
the contained value if this is a Good
if this is a Bad
Returns, if this Or
is Good
, this Good
's value; otherwise returns the result of evaluating default
.
Returns, if this Or
is Good
, this Good
's value; otherwise returns the result of evaluating default
.
the default expression to evaluate if this Or
is a Bad
the contained value, if this Or
is a Good
, else the result of evaluating the given default
Maps the given function to this Or
's value if it is a Good
or returns this
if it is a Bad
.
Maps the given function to this Or
's value if it is a Good
or returns this
if it is a Bad
.
the function to apply
if this is a Good
, the result of applying the given function to the contained value wrapped in a Good
,
else this Bad
is returned
Returns this Or
if it is a Good
, otherwise returns the result of evaluating the passed alternative
.
Returns this Or
if it is a Good
, otherwise returns the result of evaluating the passed alternative
.
the alternative by-name to evaluate if this Or
is a Bad
this Or
, if it is a
Good
, else the result of evaluating alternative
Returns an Or
with the Good
and Bad
types swapped: Bad
becomes Good
and Good
becomes Bad
.
Returns an Or
with the Good
and Bad
types swapped: Bad
becomes Good
and Good
becomes Bad
.
Here's an example:
scala> val lyrics = Bad("Hey Jude, don't make it bad. Take a sad song and make it better.") lyrics: org.scalautils.Bad[Nothing,String] = Bad(Hey Jude, don't make it bad. Take a sad song and make it better.)scala> lyrics.swap res12: org.scalautils.Or[String,Nothing] = Good(Hey Jude, don't make it bad. Take a sad song and make it better.)
Now that song will be rolling around in your head all afternoon. But at least it is a good song (thanks to swap
).
if this Or
is a Good
, its Good
value wrapped in a Bad
; if this Or
is
a Bad
, its Bad
value wrapped in a Good
.
Returns an Either
: a Right
containing the Good
value, if this is a Good
; a Left
containing the Bad
value, if this is a Bad
.
Returns an Either
: a Right
containing the Good
value, if this is a Good
; a Left
containing the Bad
value, if this is a Bad
.
Note that values effectively “switch sides” when convering an Or
to an Either
. If the type of the
Or
on which you invoke toEither
is Or[Int, ErrorMessage]
for example, the result will be an
Either[ErrorMessage, Int]
. The reason is that the convention for Either
is that Left
is used for “bad”
values and Right
is used for “good” ones.
this Good
value, wrapped in a Right
, or this Bad
value, wrapped in a Left
.
Returns a Some
containing the Good
value, if this Or
is a Good
, else None
.
Returns a Some
containing the Good
value, if this Or
is a Good
, else None
.
the contained “good” value wrapped in a Some
, if this Or
is a Good
; None
if this Or
is a Bad
.
Returns an immutable IndexedSeq
containing the Good
value, if this Or
is a Good
, else an empty
immutable IndexedSeq
.
Returns an immutable IndexedSeq
containing the Good
value, if this Or
is a Good
, else an empty
immutable IndexedSeq
.
the contained “good” value in a lone-element Seq
if this Or
is a Good
; an empty Seq
if
this Or
is a Bad
.
Returns a Try
: a Success
containing the
Good
value, if this is a Good
; a Failure
containing the Bad
value, if this is a Bad
.
Returns a Try
: a Success
containing the
Good
value, if this is a Good
; a Failure
containing the Bad
value, if this is a Bad
.
Note: This method can only be called if the Bad
type of this Or
is a subclass
of Throwable
(or Throwable
itself).
Note that values effectively “switch sides” when converting an Or
to an Either
. If the type of the
Or
on which you invoke toEither
is Or[Int, ErrorMessage]
for example, the result will be an
Either[ErrorMessage, Int]
. The reason is that the convention for Either
is that Left
is used for “bad”
values and Right
is used for “good” ones.
this Good
value, wrapped in a Right
, or this Bad
value, wrapped in a Left
.
Transforms this Or
by applying the function gf
to this Or
's Good
value if it is a Good
,
or by applying bf
to this Or
's Bad
value if it is a Bad
.
Transforms this Or
by applying the function gf
to this Or
's Good
value if it is a Good
,
or by applying bf
to this Or
's Bad
value if it is a Bad
.
the function to apply to this Or
's Good
value, if it is a Good
the function to apply to this Or
's Bad
value, if it is a Bad
the result of applying the appropriate one of the two passed functions, gf
or bf, to this Or
's value
Indicates whether this Or
is a Bad
Indicates whether this Or
is a Bad
true if this Or
is a Bad
, false
if it is a Good
.
Indicates whether this Or
is a Good
Indicates whether this Or
is a Good
true if this Or
is a Good
, false
if it is a Bad
.
Currently just forwards to filter, and therefore, returns the same result.
Represents a value that is one of two possible types, with one type being “good” and the other “bad.”
An
Or
will either be a “good” value wrapped in an instance ofGood
or a “bad” value wrapped in an instance ofBad
.The motivation for
Or
Or
differs from Scala'sEither
type in thatEither
treats both itsLeft
andRight
alternatives in an identical manner, whereasOr
treats its two alternatives differently: it favorsGood
overBad
. Because of this, it is more convenient to work withOr
s when you prefer one alternative over the other; for example, if one alternative represents a valid result and another represents an error.To illustrate, imagine you want to create instances this
Person
class from user input strings:You might write a method that parses the name from user input string and returns an
Option[String]
:None
if the string is empty or blank, else the trimmed string wrapped in aSome
:You might also write a method that parses the age from user input string and returns an
Option[Int]
:None
if either the string is not a valid integer or it is a negative integer, else the string converted to an integer wrapped in aSome
:With these building blocks you could write a method that parses name and age input strings and returns either a
Person
, wrapped in aSome
, orNone
if either the name or age, or both, was invalid:Here are some examples of invoking
parsePerson
:Now imagine you want to give an error message back if the user's input is invalid. You might rewrite the parsing methods to return an
Either
instead. In this case, the desired result is a valid name or age, which by convention should be placed on the right of theEither
. The left will be aString
error message. Here's the newparseName
function, which returns anEither[String, String]
:And here's the new
parseAge
function, which returns anEither[String, Int]
:The new
parsePerson
method will return anEither[String, Person]
:Note that
Either
requires you to add.right
at the end of each generator in thefor
expression. Although the convention is to place the valid result on the right, you must explicitly (and repetitively) indicate that you've done so by transforming theEither
to aRightProjection
by invoking.right
at each step. Given this implementation, theparsePerson
method will now short-circuit at the first sign of trouble (as it did when we used anOption
), but you now get the first error message returned in aLeft
. Here are some examples:An
Either
with “attitude”Because
Or
declares one alternative to be “good” and the other “bad,” it is more convenient thanEither
in this kind of situation. One difference to note withOr
is that theGood
alternative is on the left,Bad
on the right. The reason is thatOr
is designed to be written using infix notation, and placing the “happy path” first is more readable. For example, instead of writing:Or[Int, ErrorMessage]
You can write:
Here's how the
parseName
method might be written using anOr
, whereErrorMessage
is a type alias forString
declared in theorg.scalautils
package object:You can think of the
String
Or
ErrorMessage
result type like this:Here's how the
parseAge
method might be written:Given these implementations, here's how you'd write the
parsePerson
method:Because of
Or
's attitude, you need not write.good
at the end of each generator.Or
will keep going so long as each step produces aGood
, short circuiting at the first sign of aBad
. Here are a few invocations of thisparsePerson
method:Accumulating errors with
Or
Another difference between
Or
andEither
is thatOr
enables you to accumulate errors if theBad
type is anEvery
. AnEvery
is similar to aSeq
in that it contains ordered elements, but different fromSeq
in that it cannot be empty. AnEvery
is either aOne
, which contains one and only one element, or aMany
, which contains two or more elements.Note: an
Or
whoseBad
type is anEvery
, or one of its subtypes, is called an “accumulatingOr
.”To rewrite the previous example so that errors can be accumulated, you need first to return an
Every
as theBad
type. Here's how you'd change theparseName
method:Because
parseName
will either return a valid nameString
wrapped in aGood
, or one error message, wrapped in aBad
, you would write theBad
type asOne[ErrorMessage]
. The same is true forparseAge
:Because a
for
expression short-circuits on the firstBad
encountered, you'll need to use a different approach to write theparsePerson
method. In this example, thewithGood
method from traitAccumulation
will do the trick:Trait
Accumulation
offers overloadedwithGood
methods that take 1 to 22 accumulatingOr
s, plus a function taking the same number of correspondingGood
values. In this example, if bothname
andage
areGood
s, thewithGood
method will pass the good nameString
and ageInt
to thePerson(_, _)
function, and return the resultingPerson
object wrapped in aGood
. If eithername
andage
, or both, areBad
,withGood
will return the accumulated errors in aBad
.The result of
parsePerson
, ifBad
, will therefore contain either one or two error messages, i.e., the result will either be aOne
or aMany
. As a result, the result type ofparsePerson
must bePerson
Or
Every[ErrorMessage]
. Regardless of whether aBad
result contains one or two error messages, it will contain every error message. Here's some invocations of this accumulating version ofparsePerson
:Note that in the last example, the
Bad
contains an error message for both name and age.Other ways to accumulate errors
The
Accumlation
trait also enables other ways of accumulating errors.Using
combined
If you have a collection of accumulating
Or
s, for example, you can combine them into oneOr
usingcombined
, like this:Using
validatedBy
Or if you have a collection of values and a function that transforms that type of value into an accumulating
Or
s, you can validate the values using the function usingvalidatedBy
, like this:Using
zip
You can also zip two accumulating
Or
s together. If both areGood
, you'll get aGood
tuple containin both originalGood
values. Otherwise, you'll get aBad
containing every error message. Here are some examples:Using
when
In addition, given an accumlating
Or
, you can pass one or more validation functions towhen
on theOr
to submit thatOr
to further scrutiny. A validation function accepts aGood
type and returns aValidation[E]
, whereE
is the type in theEvery
in theBad
type. For anInt
Or
One[ErrorMessage]
, for example the validation function type would beInt
=>
Validation[ErrorMessage]
. Here are a few examples:If the
Or
on which you callwhen
is alreadyBad
, you get the same (Bad
)Or
back, because noGood
value exists to pass to the valiation functions:If the
Or
on which you callwhen
isGood
, and also passes all the validation functions (i.e., the all returnNone
), you again get the sameOr
back, but this time, aGood
one:If one or more of the validation functions fails, however, you'll get a
Bad
back contining every error. Here are some examples:Note that you can use
when
to accumulate errors in afor
expression involving an accumulatingOr
, like this:Much ado about
Nothing
Because
Or
has two types, but each of its two subtypes only takes a value of one or the other type, the Scala compiler will inferNothing
for the unspecified type:Often
Nothing
will work fine, as it will be widened as soon as the compiler encounters a more specific type. Sometimes, however, you may need to specify it. In such situations you can use this syntax:If you want to specify both types, because you don't like the inferred type, you can do so like this:
But you may find the code is clearer if you instead use a type ascription, like this:
Note: The
Or
hierarchy was inspired in part by the disjoint union (\/
) andValidation
types ofscalaz
, theProcessResult
type of Typesafe Activator, and theResult
type of ScalaKittens.