ReactiveX / IxJS

The Interactive Extensions for JavaScript
https://reactivex.io/IxJS/
MIT License
1.32k stars 74 forks source link

Curried and args switched #23

Open dacz opened 7 years ago

dacz commented 7 years ago

Would be nice to have some methods curried (and because of that switched arguments) to align with FP style.

Example:

// Current
const results = of(1, 2, 3)
  .chain(source => filter(source, x => x % 2 === 0))
  .chain(source => map(source, x => x * x));

// Possible with args switched (and curried)
const results = of(1, 2, 3)
  .chain(filter(x => x % 2 === 0))
  .chain(map(x => x * x));
mattpodwysocki commented 7 years ago

@dacz I agree it would be nice, will investigate for an FP variant

graingert commented 7 years ago

can we also support _.flow or provide a flow/compose function:

// Current
const results = of(1, 2, 3)
  .chain(source => filter(source, x => x % 2 === 0))
  .chain(source => map(source, x => x * x));

import * as ix from 'ix/fp/es';
import flow from 'lodash.flow';
// Possible with args switched (and curried)
const results = ix.flow(
  v => ix.of(...v),
  ix.filter(x => x % 2 === 0),
  ix.map(x => x * x),
)([1, 2, 3]);

assert(ix.flow === flow);
mattpodwysocki commented 6 years ago

@dacz @graingert is the pipe mechanism we have good enough or do we need more?

graingert commented 6 years ago

@mattpodwysocki I'd like to be able to use a flow function to create other functions:

import * as ix from 'ix/fp/es';

const process = ix.flow(
  v => ix.of(...v),
  ix.filter(x => x % 2 === 0),
  ix.map(x => x * x),
);

process([1, 2, 3]); // a new iterable.

(eg I'd like to be able to use the whole of IxJS without ever calling a method, or worrying about this)

graingert commented 6 years ago

and if/when |> is released I'd like to be able to use that.


import * as ix from 'ix/fp/es';

const process = v => ix.of(...v)
  |> ix.filter(x => x % 2 === 0)
  |> ix.map(x => x * x)
);

process([1, 2, 3]); // a new iterable.
mattpodwysocki commented 6 years ago

@graingert that's already supported since source would be the first applied argument as described in the proposal

import { AsyncIterableX as AsyncIterable } from 'ix';
import { map, filter } from 'ix/asynciterable';

const process = v => AsyncIterable.of(...v)
  |> _ => filter(_, x => x % 2 === 0)
  |> _ => map(_, x => x * x)
);

process([1, 2, 3]);

Or you can use the pipe operators.

import { AsyncIterableX as AsyncIterable } from 'ix';
import { map, filter } from 'ix/asynciterable/pipe';

const process = v => AsyncIterable.of(...v)
  |> filter(x => x % 2 === 0)
  |> map(x => x * x)
);

process([1, 2, 3]);
graingert commented 6 years ago

@mattpodwysocki ah good stuff. how about a single namespace to import from?

mattpodwysocki commented 6 years ago

@graingert that's why we have an index.ts in each area root for just that purpose, as well as the piped.

graingert commented 6 years ago

@mattpodwysocki I mean the whole library in fp/pipe form. Currently you're doing it like:

import { AsyncIterableX as AsyncIterable } from 'ix';
import { map, filter } from 'ix/asynciterable/pipe';

I want:

import { * as ix } from 'ix/fp';
trxcllnt commented 6 years ago

@graingert the present structure is in place to support all three styles. At a minimum operators for the iterable/asyncIterable will stay separate, since they share names. Importing everything from the root is convenient, but won't tree shake.