Replies: 9 comments 10 replies
-
Hi @jam-fran , I just had a scenario where I felt a similar need but ended up solving it with the built-in methods. ScenarioI have a const getData = map(
sequence(getUser, getUserAssessments),
mergeObjects,
); All good. Now I have a new requirement that imposes a challenge: I want to be able to override some properties of Here is my solution:First I start changing my getUserAssessments' inputSchema: // from this
z.object({ user: userSchema })
// to this
z.object({ user: userSchema, overrides: partialUserSchema.optional() }) Now all I need to do is to create a const passthrough: makeDomainFunction(z.any())((input) => input) Now I can merge the const getUserPassthrough = map(all(getUser, passthrough), mergeObjects)
const getData = map(
sequence(getUserPassthrough, getUserAssessments),
mergeObjects,
);
// than in my loader:
export async function loader({ request, params }: DataFunctionArgs) {
const result = await getData({ ...params, overrides: inputFromUrl(request) });
// ...
} And I can now change the user properties by using a query string: That's itI hope it helps you finding a nice way to forward your input to sequential domain functions ;) |
Beta Was this translation helpful? Give feedback.
-
@jam-fran is the sequencing important in this case because of the order of the side-effects? |
Beta Was this translation helpful? Give feedback.
-
Hey hey! I'd a similar use-case as the two examples above. I have a sequence of functions (not parallel), but I need to merge the output of the "previous function" with the input for the "next function", and it should return a single merged output of all functions. I needed something like this: it("Should aggregate the output on the input of the next function", async () => {
const input = { a: 1 };
const dfReturnsB = makeDomainFunction(z.any())(() => ({
b: 2,
}));
const dfReturnsC = makeDomainFunction(
z.object({ a: z.number(), b: z.number() })
)(({ a, b }) => ({ a, b, c: 3 }));
const dfReturnsD = makeDomainFunction(z.any())(() => ({
d: 4,
}));
const aggregatedDomainFunction = aggregate(
dfReturnsB,
dfReturnsC,
dfReturnsD
);
const aggregationResult = await fromSuccess(aggregatedDomainFunction)(input);
expect(aggregationResult).toEqual({
a: 1,
b: 2,
c: 3,
d: 4,
});
}); And I have ended up with the following function: import {
DomainFunction,
Result,
SuccessResult,
UnpackAll,
mergeObjects,
} from "domain-functions";
function aggregate<Fns extends DomainFunction[]>(
...fns: Fns
): DomainFunction<UnpackAll<Fns>> {
return async function (initialInput: unknown, environment?: unknown) {
const results = [];
let inputs = [initialInput];
let prevResult: undefined | Result<unknown>;
for await (const fn of fns as DomainFunction[]) {
if (prevResult?.success) inputs.push(prevResult.data);
const result = await fn(mergeObjects(inputs), environment);
if (!result.success) return result;
prevResult = result;
results.push(result.data);
}
return {
success: true,
data: mergeObjects(results),
inputErrors: [],
environmentErrors: [],
errors: [],
} as SuccessResult<UnpackAll<Fns>>;
};
}
export { aggregate }; Maybe this approach can help with your cases @jam-fran @gustavoguichard 🙂 Also I would be glad to contribute to the repo, if this function makes sense for the library. |
Beta Was this translation helpful? Give feedback.
-
I'm reopening this issue as I've been stumbling upon this necessity so I want to investigate if @atreib 's solution could be a good addition to this library. |
Beta Was this translation helpful? Give feedback.
-
couldn't this be built with a sequence + map? |
Beta Was this translation helpful? Give feedback.
-
@diogob I don't think so 🤔 Because with the @gustavoguichard Yeah, 100%! |
Beta Was this translation helpful? Give feedback.
-
@atreib could you solve your use case moving this initial input to the environment? In this way the whole composition would have access to it and you still have the output from the previous function in a |
Beta Was this translation helpful? Give feedback.
-
Folks, I turned this issue into a discussion. I was talking to @diogob and explaining him about a possible scenario where the Scenario:We want to
How to solve that with the current combinators?It is possible currently! Premises
Here is an const identity = (input?: any) => ({ success:true, data:input, inputErrors: [], errors: [], environmentErrors: []}) as any; SolutionTo solve the proposed scenario under the given premises this is a possible solution to the problem: // First we change the structure of our dfs a lil bit so the first input `x` becomes environment:
const envSchema = z.object({ x: z.number() });
const dfA = mdf(z.any(), envSchema)((_, { x }) => ({ a: x * 2 }));
const dfB = mdf()(() => ({ b: 5 }));
const dfC = mdf(z.object({ a: z.number(), b: z.number() }))(({ a, b }) => ({
c: a + b,
}));
const dfD = mdf(
z.object({ b: z.number() }),
envSchema
)(({ b }, { x }) => ({ d: x + b })); Now, with the help of const data = map(
sequence(dfA, merge(dfB, identity), merge(dfC, identity), dfD),
mergeObjects
); What should we add to the library?We can do that composition with our current methods, but should we add more combinators to avoid the boilerplate? I'd export const identity = (input?: any) => ({ success:true, data:input, inputErrors: [], errors: [], environmentErrors: []}) as any;
const passthrough = <T extends DomainFunction<Record<string, unknown>>>(df: T) => merge(df, identity)
const data = map(
sequence(dfA, passthrough(dfB), passthrough(dfC), dfD),
mergeObjects
); What do you guys think? |
Beta Was this translation helpful? Give feedback.
-
@gustavoguichard rereading the original post here, it seems that the upcoming |
Beta Was this translation helpful? Give feedback.
-
Hi! I've run into a few use cases where I'd like to combine 2 or more domain functions where:
pipe
orsequence
)and
orcollect
)I might be missing something or misreading the docs - is this currently possible? If not, any ideas for a workaround or elegant way to handle this? Thank you!
Beta Was this translation helpful? Give feedback.
All reactions