-
Notifications
You must be signed in to change notification settings - Fork 109
Open
Labels
documentationImprovements or additions to documentationImprovements or additions to documentation
Description
more empiric evidence for pipe’s impact would be good. (from #232 (comment))
Thanks so much for persevering with this proposal. Here's 8 snippets (I could probably provide hundreds more) from our small company's codebase showing how Pipeline could make it easier to use one common API: Object.fromEntries
.
A few surprising (to me) observations from this exercise:
- Ironically, the biggest usability win was NOT actually moving
Object.entries
to use Pipeline. Instead, a much larger win was removing calls toreduce
(see the last 2 snippets) that were only in our code becausereduce
is chainable andObject.entries
is not. Apparently, some developers at our company love chaining so much that they were willing to write much more code (and much more complicated code) just to avoid having to use a nested function call that breaks method chaining. 🤔 It'd be fun to see a plenary slide that includes the line "Pipeline will reducereduce
". 😄 - Pipeline and Prettier will likely reinforce each other to improve readability. With nested function calls, each call is indented and moved to a new line by Prettier, and all the extra indents in turn cause function calls to exceed Prettier's line length causing their arguments to also be split across lines. Method chains however (including pipes) are moved to new lines at the same nesting level. The result with Pipeline is not only clearer code that reads right-to-left and top-to-bottom, but it's also less indented so each chained call is much less likely to be split across multiple lines, further improving readability.
- We don't really use FP. In our codebase, Pipeline would simply make our existing, non-FP code easier to read and easier to write. You don't need to love FP to love Pipeline.
Note that Object.entries
is not an outlier. I could provide a similar laundry list of snippets for all popular constructors and factory methods like [...new Set(array)]
(for deduping arrays), Temporal.*.from
, Array.from
, etc. And that's just builtins; any class constructor or factory method in our own code or in libraries we use would get easier to use chainably.
// current
return Object.fromEntries(
Object.entries(value).map(([k, v]) => [typeof v === 'string' ? `${k}.untranslated` : k, transformValue(v)])
);
// with Pipeline
return Object.entries(value)
.map(([k, v]) => [typeof v === 'string' ? `${k}.untranslated` : k, transformValue(v)])
|> Object.fromEntries(%);
// current
const result = Object.fromEntries(
Object.entries(grouped).map(([machineId, attempts]) => [
machineId,
attempts[0],
])
);
// with Pipeline
const result = Object.entries(grouped)
.map(([machineId, attempts]) => [machineId, attempts[0]])
|> Object.fromEntries(%);
// current
const trimmed = Object.fromEntries(
Object.entries(payload.message).map(([key, value]) => [
key,
value ? value.trim() : unknownLabel,
]),
);
// with Pipeline
const trimmed = Object.entries(payload.message)
.map(([key, value]) => [key, value ? value.trim() : unknownLabel])
|> Object.fromEntries(%);
// current
const MdIconsTransformedKeys = Object.fromEntries(
Object.entries(MdIcons).map(([iconName, icon]) => [
transformIconName(iconName),
icon,
]);
// with Pipeline
const MdIconsTransformedKeys = Object.entries(MdIcons)
.map(([iconName, icon]) => [transformIconName(iconName), icon])
|> Object.fromEntries(%);
// current
return Object.fromEntries(
this.$storeTS.state.printer.printer.material_manager.materials_list.map(m => [m.name, m.color])
);
// with Pipeline
return this.$storeTS.state.printer.printer.material_manager.materials_list
.map(m => [m.name, m.color])
|> Object.fromEntries(%);
// current
return Object.entries(localStorage)
.map(([k, v]) => [String(k), v])
.filter(([k, _v]) => k.startsWith(FLAG_PREFIX))
.reduce(
(acc, [k, v]) => ({ ...acc, [k.slice(FLAG_PREFIX.length)]: !!v }),
{}
);
// with Pipeline
return Object.entries(localStorage)
.map(([k, v]) => [String(k), v])
.filter(([k, _v]) => k.startsWith(FLAG_PREFIX))
.map([k, v]) => [k.slice(FLAG_PREFIX.length), !!v])
|> Object.fromEntries(%);
// current
return Object.entries(colors)
.toSorted(([_nameA, colorA], [_nameB, colorB]) => {
const lightnessA = parseToHsl(colorA).lightness;
const lightnessB = parseToHsl(colorB).lightness;
return lightnessA - lightnessB;
})
.reduce((acc, [name, color]) => {
acc[name] = color;
return acc;
}, {} as Record<string, string>);
// with Pipeline
return Object.entries(colors)
.toSorted(([_nameA, colorA], [_nameB, colorB]) => {
const lightnessA = parseToHsl(colorA).lightness;
const lightnessB = parseToHsl(colorB).lightness;
return lightnessA - lightnessB;
})
|> Object.fromEntries(%) as Record<string, string>);
// current
return Object.keys(state.availableCommands)
.filter((key) => key.endsWith('CALIBRATE'))
.reduce((o, key) => {
return {
...o,
[key]: state.availableCommands[key],
};
}, {} as GcodeCommands);
// with Pipeline
return Object.keys(state.availableCommands)
.filter((key) => key.endsWith('CALIBRATE'))
.map((key) => [key, state.availableCommands[key]]
|> Object.fromEntries(%) as GcodeCommands);
haltcase, tabatkins, gustavopch, kands-code, ljharb and 4 moreVictorGaiva
Metadata
Metadata
Assignees
Labels
documentationImprovements or additions to documentationImprovements or additions to documentation