gigobyte / purify

Functional programming library for TypeScript - https://gigobyte.github.io/purify/
ISC License
1.5k stars 59 forks source link

What is the correct way to write this? #612

Closed borodinalive closed 1 year ago

borodinalive commented 1 year ago
async execute(state: StateDto): Promise<NetworkError | SellerDetailsDto> {
    return await this.stateManager?.getUser(state)
      .map((user) => user.seller?.id)
      .map(async (sellerId) => {
        return (await this.userService.getUserSeller(sellerId ?? ''))
          .extract();
      })
      .extract();
  }

It's seems to me, that there is a problem in second map. I suppose that it must be a method like flatMap with async function getting a parameter and returning another EITHER. And this flatMap must return an flattened EITHER with result of function inside flatMap. The most close method is chain, but it refuse to work with promises with an error: Argument of type '(sellerId: string | undefined) => Promise<NetworkError | SellerDetailsDto>' is not assignable to parameter of type '(value: string | undefined) => Either<unknown, unknown>'.

So. How do it the right way?

gigobyte commented 1 year ago

Either only works synchronously, even if you use async/await inside map it's still a Promise and it's much like using async/await inside Array.map - it doesn't work.

I suggest you look at the EitherAsync docs, the API is exactly the same as Either but you can work with Promises and async/await.

borodinalive commented 1 year ago

The most funny thing is it code works :)) and Idea's linter is OK with it. But not with chain

Is it right?

  async execute(state: StateDto): Promise<NetworkError | SellerDetailsDto> {
    return await this.stateManager?.getUser(state)
      .map((user) => user.seller?.id)
      .map(async (sellerId) => {
        return EitherAsync(() => this.userService.getUserSeller(sellerId ?? ''));
      })
      .extract();
  }

So it works too. :-))

gigobyte commented 1 year ago

Without more context I can't help you much, what does this.stateManager?.getUser(state) return? An Either, Maybe, EitherAsync? I would prefer a codepen/stackblitz etc with code I can work with.

borodinalive commented 1 year ago

Unfortunately I don't know how to use it. There was no need earlier. :((

this.stateManager?.getUser(state) returns Either<any, UserDto> this.userService.getUserSeller(sellerId ?? '') returns Promise<Either<NetworkError, SellerDetailsDto>>

gigobyte commented 1 year ago

Based on what you've given me here's how the implementation must look like:

interface User { sellerId: number }
interface SellerDetails { id: number }

declare const getUser: () => Either<Error, User>
declare const getUserSeller: (sellerId: number) => Promise<Either<Error, SellerDetails>>

const execute = (): Promise<Error | SellerDetails> =>
  EitherAsync.liftEither(getUser())
    .map((user) => user.sellerId)
    .chain((sellerId) => getUserSeller(sellerId))
    .run()
    .then((either) => either.extract())

Hope that helps! The key here is that if you have an Either and you want to do async stuff with it you can use EitherAsync.liftEither and then you're free to chain Promises. Also if you decide to return EitherAsync directly it's less code to wrap and unwrap values:

const execute = (): EitherAsync<Error, SellerDetails> =>
  EitherAsync.liftEither(getUser())
    .map((user) => user.sellerId)
    .chain((sellerId) => getUserSeller(sellerId))

const result: Either<Error, SellerDetails> = await execute()
borodinalive commented 1 year ago

It is a HUUUGE HELP! Thank you very much!!!

Real world examples are extremely needed!

That's the final working result:

  async execute(state: StateDto): Promise<NetworkError | SellerDetailsDto> {
    return EitherAsync.liftEither(this.stateManager.getUser(state))
      .map((user) => user.seller?.id)
      .chain((sellerId) => this.userService.getUserSeller(sellerId ?? ''))
      .run()
      .then((either) => either.extract());
  }
}

BTW If my first WORKING use case is not expectable it is ponentialy a bug or a promising feature! ;-)