4

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?

Ivan Tarasov
  • 85
  • 1
  • 3
  • `E.Either` doesn't make much sense because the type is `Either`, so you cannot get an `Either` anyway. The reason why you lose the `Either` layer is the natural transformation `TE.fromEither(item)` from `Either` to `Task` you perform as the first step of your composition. –  May 02 '20 at 13:42
  • Oops, just noticed that your initial Either has literals as type parametrs and hence is `Either`. The Issue remains the same though. –  May 02 '20 at 13:45
  • @bob yes, I user natural transformation to make it work, because I don't find a way to write correct pipe, that will return `E.Either` or `Option`, which is not a point, in the right part, and put Error from the second request to Left part - it's what my question about. – Ivan Tarasov May 02 '20 at 16:02

1 Answers1

3

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)
);
MnZrK
  • 1,330
  • 11
  • 23
  • It works thank you! But how to get the correct types? I tried this `A.sequenceT(TE.taskEitherSeq)(result)`, but in return, I get Array type `TE.TaskEither]>`, which is not what I am looking for :( Do you know how to solve that? – Ivan Tarasov May 03 '20 at 08:26
  • @IvanTarasov Not exactly sure, maybe there is an error or a typo. I updated the answer with some typing suggestions, you can work from there, it should give you some sensible compiler errors. – MnZrK May 03 '20 at 20:08