Skip to content
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

Proposal: add pipeable versions of functions like toArray() or reduce() #294

Open
millimoose opened this issue Feb 19, 2020 · 0 comments
Open

Comments

@millimoose
Copy link

millimoose commented Feb 19, 2020

Currently, only functions that both take and return an Iterable (or AsyncIterable) are available as pipeable operators; functions that take an Iterable and return some "scalar" value are not.

This means that e.g. code that uses piping that needs to return an array - which is common in my codebase because very little third-party JS code works with Iterables - has to be written like this:

selectedCheckboxes(): string[] {
	const result = ix.from(Object.entries(this.form.controls)).pipe(
		ixop.filter(([, ctl]) => !!ctl.value),
		ixop.map(([key]) => key),
	);
	return ix.toArray(result);
}

as opposed to, hypothetically, like this:

selectedCheckboxes(): string[] {
	ix.from(Object.entries(this.form.controls)).pipe(
		ixop.filter(([, ctl]) => !!ctl.value),
		ixop.map(([key]) => key),
		ixop.toArray(),
	);
}

(Example is getting the keys of selected checkboxes from an Angular form.)

This can be worked around by using a utility library with a more generic pipe(), and in a future an unspecified time away may be made irrelevant by the upcoming native pipe operator proposals, but it'd be nice if Ix provided this in the meantime, given the overall benefits of piping for readability and chaining.

Using reduce() as an example function to be converted, I propose:

  1. Adding a new operator type:
type TerminalOperator<T, R> = UnaryFunction<Iterable<T>, R>
  1. Adding new overloads to pipe() that add a parameter of this type at the end, e.g.:
function pipe<T, A, R>(source: Iterable<T>, op1: OperatorFunction<T, A>, term: TerminalOperator<A, R>): R;
  1. Adding operator versions of functions that take an Iterable as their first parameter but don't return an Iterable:

ix/iterable/operators/reduce.ts

import { reduce as _reduce } from '../reduce';
function reduce<T, R = T>(
  accumulator: (previousValue: R, currentValue: T, currentIndex: number) => R,
  seed?: R
): TerminalOperator<T, R> {
  return function reduceOperatorFunction(source: Iterable<T>) {
    return _reduce(source, accumulator, seed);
  };
}

It's possible to eliminate some of the gruntwork in the last step by using a generic function like:

function toOperator<T, F extends (first: Iterable<T>, ...rest: any[]) => any>(
	fn: F
): (...rest: any[]) => TerminalOperator<T, ReturnType<F>> {
	return (...rest) => first => fn(first, ...rest);

but it seems impossible to use it as a workaround directly, Typescript is unable to infer the types correctly; therefore it's necessary to provide the actual signatures of the resulting operators for this to be of much use.

Given that this is mostly mechanical work, I might be up to actually contributing this if there's interest, but for obvious reasons I'd like to gauge that before making a huge PR.

@millimoose millimoose changed the title Proposal: add pipeable versions of functions like toArray or 'reduce' Proposal: add pipeable versions of functions like toArray() or reduce() Feb 19, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant