Ivan Tarasov
Ivan Tarasov

Reputation: 85

Chain fp-ts TaskEither with Either in right

I have a flow of 2 nested request, where could be 3 different results:

  1. One of requests return Error
  2. User is not Anonymous, return Profile
  3. User is Anonymous, return false

Both requests could throw an error, and becaues of that implements TaskEither

const isAuth = ():TE.TaskEither<Error, E.Either<true, false>>  
   => TE.tryCatch(() => Promise(...), E.toError)
const getProfile = ():TE.TaskEither<Error, Profile>  
   => TE.tryCatch(() => Promise(...), E.toError)

The first request returns the boolean status of user authorization. Second request loads user profile if the user is authorized.

In return, I want to get the next signature, Error or Either with Anonymous/Profile:

E.Either<Error, E.Either<false, Profile>>

I tried to make it this way:

pipe(
    isAuth()
    TE.chain(item => pipe(
      TE.fromEither(item),
      TE.mapLeft(() => Error('Anonimous')),
      TE.chain(getProfile)
    ))
  )

But in return, I get E.Either<Error, Profile>, witch not convenient because I have to extract Anonymous status by hands from Error.

How to solve that question?

Upvotes: 4

Views: 4534

Answers (1)

MnZrK
MnZrK

Reputation: 1380

Don't know if you oversimplify actual code, but E.Either<true, false> is isomorphic to just boolean, so let's stick with simpler thing.

declare const isAuth: () => TE.TaskEither<Error, boolean>;
declare const getProfile: () => TE.TaskEither<Error, Profile>;

Then you add condition branch based on whether its authed or not and wrap the result of getProfile:

pipe(
  isAuth(),
  TE.chain(authed => authed 
    ? pipe(getProfile(), TE.map(E.right)) // wrap the returned value of `getProfile` in `Either` inside the `TaskEither`
    : TE.right(E.left(false))
  )
)

This expression has type TaskEither<Error, Either<false, Profile>>. You probably need to add some type annotations for it to typecheck properly, I haven't run the code myself.

EDIT:

You probably need to extract lambda as named function to get proper typings, something like so:

const tryGetProfile: (authed: boolean) => TE.TaskEither<Error, E.Either<false, Profile>> = authed
  ? pipe(getProfile(), TE.map(E.right))
  : TE.right(E.left(false));

const result: TE.TaskEither<Error, E.Either<false, Profile>> = pipe(
  isAuth(),
  TE.chain(tryGetProfile)
);

Upvotes: 4

Related Questions