Either
On this page
The Either
data type represents two exclusive possible values: an Either<R, L>
can be either a Right
value or a Left
value, where R
represents the type of the Right
value and L
represents the type of the Left
value.
Creating Eithers
You can create an Either
using the Either.right
and Either.left
constructors.
-
The
Either.right
function takes a value of typeR
and constructs aEither<R, never>
:tsimport {Either } from "effect"constrightValue =Either .right (42)tsimport {Either } from "effect"constrightValue =Either .right (42) -
The
Either.left
function takes a value of typeL
and constructs aEither<never, L>
:tsimport {Either } from "effect"constleftValue =Either .left ("not a number")tsimport {Either } from "effect"constleftValue =Either .left ("not a number")
Guards
You can determine whether an Either
is a Left
or a Right
by using the Either.isLeft
and Either.isRight
guards:
ts
import {Either } from "effect"constfoo =Either .right (42)if (Either .isLeft (foo )) {console .log (`The left value is: ${foo .left }`)} else {console .log (`The Right value is: ${foo .right }`)}// Output: "The Right value is: 42"
ts
import {Either } from "effect"constfoo =Either .right (42)if (Either .isLeft (foo )) {console .log (`The left value is: ${foo .left }`)} else {console .log (`The Right value is: ${foo .right }`)}// Output: "The Right value is: 42"
Pattern Matching
The Either.match
function allows you to handle different cases of an Either
by providing separate callbacks for each case:
ts
import {Either } from "effect"constfoo =Either .right (42)constmessage =Either .match (foo , {onLeft : (left ) => `The left value is: ${left }`,onRight : (right ) => `The Right value is: ${right }`})console .log (message ) // Output: "The Right value is: 42"
ts
import {Either } from "effect"constfoo =Either .right (42)constmessage =Either .match (foo , {onLeft : (left ) => `The left value is: ${left }`,onRight : (right ) => `The Right value is: ${right }`})console .log (message ) // Output: "The Right value is: 42"
Using match
instead of isLeft
or isRight
can be more expressive and
provide a clear way to handle both cases of an Either
.
Working with Either
Once you have an Either
, there are several operations you can perform on it.
Mapping over the Right Value
You can use the Either.map
function to transform the Right
value of an Either
.
The map
function takes a transformation function that maps the Right
value.
If the Either
value is a Left
value, the transformation function is ignored, and the Left
value is returned unchanged.
Example
ts
import {Either } from "effect"Either .map (Either .right (1), (n ) =>n + 1) // right(2)Either .map (Either .left ("not a number"), (n ) =>n + 1) // left("not a number")
ts
import {Either } from "effect"Either .map (Either .right (1), (n ) =>n + 1) // right(2)Either .map (Either .left ("not a number"), (n ) =>n + 1) // left("not a number")
Mapping over the Left Value
You can use the Either.mapLeft
function to transform the Left
value of an Either. The mapLeft
function takes a transformation function that maps the Left
.
If the Either
value is a Right
value, the transformation function is ignored, and the Right
value is returned unchanged.
Example
ts
import {Either } from "effect"Either .mapLeft (Either .right (1), (s ) =>s + "!") // right(1)Either .mapLeft (Either .left ("not a number"), (s ) =>s + "!") // left("not a number!")
ts
import {Either } from "effect"Either .mapLeft (Either .right (1), (s ) =>s + "!") // right(1)Either .mapLeft (Either .left ("not a number"), (s ) =>s + "!") // left("not a number!")
Mapping over Both Values
You can use the Either.mapBoth
function to transform both the Left
and Right
values of an Either
. The mapBoth
function takes two transformation functions: one for the Left
value and one for the Right
value.
Example
ts
import {Either } from "effect"Either .mapBoth (Either .right (1), {onLeft : (s ) =>s + "!",onRight : (n ) =>n + 1}) // right(2)Either .mapBoth (Either .left ("not a number"), {onLeft : (s ) =>s + "!",onRight : (n ) =>n + 1}) // left("not a number!")
ts
import {Either } from "effect"Either .mapBoth (Either .right (1), {onLeft : (s ) =>s + "!",onRight : (n ) =>n + 1}) // right(2)Either .mapBoth (Either .left ("not a number"), {onLeft : (s ) =>s + "!",onRight : (n ) =>n + 1}) // left("not a number!")
Interop with Effect
The Either
type is a subtype of the Effect
type, which means that it can be seamlessly used with functions from the Effect
module. These functions are primarily designed to work with Effect
values, but they can also handle Either
values and process them correctly.
In the context of Effect
, the two members of the Either
type are treated as follows:
Left<L>
is equivalent toEffect<never, L>
Right<R>
is equivalent toEffect<R>
To illustrate this interoperability, let's consider the following example:
ts
import {Effect ,Either } from "effect"consthead = <A >(array :ReadonlyArray <A >):Either .Either <A , string> =>array .length > 0 ?Either .right (array [0]) :Either .left ("empty array")constfoo =Effect .runSync (Effect .flatMap (Effect .succeed ([1, 2, 3]),head ))console .log (foo ) // Output: 1constbar =Effect .runSync (Effect .flatMap (Effect .succeed ([]),head )) // throws "empty array"
ts
import {Effect ,Either } from "effect"consthead = <A >(array :ReadonlyArray <A >):Either .Either <A , string> =>array .length > 0 ?Either .right (array [0]) :Either .left ("empty array")constfoo =Effect .runSync (Effect .flatMap (Effect .succeed ([1, 2, 3]),head ))console .log (foo ) // Output: 1constbar =Effect .runSync (Effect .flatMap (Effect .succeed ([]),head )) // throws "empty array"