T. Arafat
T. Arafat

Reputation: 55

Merging an Option monad into IOEither?

I was trying to write a localStorage wrapper in fp-ts when I ran into a roadblock. I want to handle null values as well as exceptions thrown by localStorage, so I started with this code:

import * as IOE from "fp-ts/IOEither";
import * as O from "fp-ts/Option";

const getItem = (key: string): IOE.IOEither<Error, O.Option<string>> =>
  IOE.tryCatch(
    () => O.fromNullable(localStorage.getItem(key)),
    E.toError
  )

The function above has a return signature of IOEither<Error, Option<string>>. I want to merge the Option into the IOEither, ie, get a IOEither<Error, string>. How would I achieve this?

P.S. I suppose the above problem is also relevant in the case of TaskEither<Error, Option<string>>.

Upvotes: 0

Views: 313

Answers (1)

Lauren Yim
Lauren Yim

Reputation: 14098

You could use something like this:

import * as E from "fp-ts/Either";
import * as IOE from "fp-ts/IOEither";
import * as O from "fp-ts/Option";
import {pipe} from "fp-ts/function";

const getItem = (key: string): IOE.IOEither<Error, string> =>
  pipe(
    IOE.tryCatch(() => O.fromNullable(localStorage.getItem(key)), E.toError),
    IOE.chainEitherK(E.fromOption(() => new Error("key does not exist")))
  );

IOE.chainEitherK(f) is equivalent to IO.map(E.chain(f)):

export declare const chainEitherK:
  <E, A, B>(f: (a: A) => Either<E, B>) => (ma: IOEither<E, A>) => IOEither<E, B>

E.fromOption converts an Option<A> into an Either<E, A> given a default error value if the option is None:

export declare const fromOption:
  <E>(onNone: Lazy<E>) => <A>(ma: Option<A>) => Either<E, A>

Upvotes: 2

Related Questions