-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Memoize functions for Task and TaskEither #148
Comments
Memoization of Task could be done this way: import { memoize as memoizeF } from "fp-ts-std/Function";
import { memoize as memoizeIO } from "fp-ts-std/IO";
import type { Eq } from "fp-ts/Eq";
import { flow } from "fp-ts/function";
import type { Task } from "fp-ts/Task";
export const memoizeTask = memoizeIO as <A>(f: Task<A>) => Task<A>;
export const memoizeTaskK =
<A>(eq: Eq<A>) =>
<B>(f: (x: A) => Task<B>) =>
memoizeF(eq)(flow(f, memoizeTask)); Not sure about memoizing TE though. |
@stevebluck what problems do you encounter? We would currently use something like this: import { Eq } from 'fp-ts/Eq';
import * as M from 'fp-ts/Map';
import * as O from 'fp-ts/Option';
import type { Predicate } from 'fp-ts/Predicate';
import { Task } from 'fp-ts/Task';
import { pipe } from 'fp-ts/function';
export const memoizeK =
<A>(eq: Eq<A>) =>
<B>(f: (a: A) => Task<B>, shouldCache: Predicate<B>): ((a: A) => Task<B>) => {
const cache = new Map<A, Promise<B>>();
return (a) =>
pipe(
cache,
M.lookup(eq)(a),
O.fold(
() => () => {
const p = f(a)();
cache.set(
a,
p.then((b) => {
if (shouldCache(b) === false) {
cache.delete(a);
}
return b;
}),
);
return p;
},
(p) => () => p,
),
);
}; Then you can use the function like this const memoized = memoizeK(Str.Eq)((a: string) => TE.right(a), E.isLeft); |
I did this gcanti/fp-ts-contrib#84. |
@DenisFrezzato Wouldn't that be susceptible to a race condition for async types? i.e. I'd anticipate task memoisation reusing an in-flight promise. |
@mlegenhausen this is perfect and is what I was looking for. In my case I have a TaskEither called I have adapted your function to work with my use case but I wonder if there is a way to do it without passing in any arguments? I guess we would just hardcode the cache key. |
You can infer a export const memoize = <B>(f: Task<B>, shouldCache: Predicate<B>): Task<B> => memoizeK({ equals: constTrue })(() => f, shouldCache)(undefined); |
I have found the need to cache some data that is produced asynchronously. It would be great to have the memoize function available in an async context.
With a TaskEither I wouldn't want to cache the Left though. Maybe something similar can be done with what's in Function/memoize with the
eq
or a predicate akaE.isRight
?I have tried implementing this myself but tripped up around race conditions.
Something along the lines of:
Not sure if it's possible without a concrete implementation around TaskEither's.
The text was updated successfully, but these errors were encountered: