gigobyte / purify

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

Different Behavior Between EitherAsync .chain(x => x) and .join() #622

Closed TheWix closed 1 year ago

TheWix commented 1 year ago

So, I have something similar to the following:

declare const getUserId: () => EitherAsync<string, number>;
declare const fileRequest: Either<string, FileRequest>;
declare const processFile: (userId: number) => (file: FileRequest) => EitherAsync<string, void>;

const workflow = getUserId().map(processFile);

const result = EitherAsync.liftEither(fileRequest).ap(workflow).chain(x => x);

Which types out fine. The result is EitherAsync<string, void>

however, when I replace the chain(identity) with join() I get a compiler error: image


  Type 'EitherAsync<string, void>' is missing the following properties from type 'Either<unknown, unknown>': isLeft, isRight, toJSON, inspect, and 11 more.```

It seems to expect the nester `Right` type to be an `Either` rather than an `EitherAsync`. 

On a separate note, `ap` seems awkward to use. Am I using it incorrectly or is there a better way? 
gigobyte commented 1 year ago

That is correct, as per the docs: e.join() is equivalent to e.chain(async x => x) <-- notice the async part. The reason for that is that it's not possible to have an EitherAsync inside EitherAsync because it's a wrapper around Promise and promises automatically "join".

The solution here is to fix the type of ap because result has a type of EitherAsync<string, EitherAsync<string, void>> which is not correct. Thanks for reporting, I'll try to resolve it shortly!

gigobyte commented 1 year ago

Regarding ap, you are correct that is it awkward, it's not really suited for the object.method() instead of method object nature of purify.

In practice this means

-- Haskell
User <$> parseUsername username <*> parsePassword password <*> parseEmail email

becomes

// JS
parseEmail(email).ap(parsePassword(password).ap(parseUsername(username).map(User)))

Although I remember there was a more beautiful way to write the expression above, but I could be wrong.

gigobyte commented 1 year ago

Fixed in v1.3.5

TheWix commented 1 year ago

Wow, quick turnaround! Thank you for pointing out my error, as well!

I also want to thank you for putting this library together. I was using fp-ts before, but I found it was very difficult for people new to FP to pick it up. This library is not only a great way to learn the basics, but I have found it a great library to use in my day-to-day job.

I will hopefully be shipping some code with this shortly and then I can add my employer to your list of companies using it in Production!