-
Notifications
You must be signed in to change notification settings - Fork 206
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
Curry "statics" #20
Comments
I think currying and its benefits have made enough headway in JS (for example, Ramda has become quite popular, and lodash-fp is actively developed) that it's time to do this. |
Another angle on this is: What value are the static functions, like |
In theory, I'd like to see purely functional reactive library in JS. Currying turns out to be extremely useful in practice. I use Ramda extensively and one of the main reason for it is currying. Streaming libraries generally does not provide static counterparts for Promises. And mixed mess of methods and functions is anything but pretty. It shouldn't be hard to implement though. import {curry, pipe} from "ramda";
let M = {
then: curry((handler, promise) => {
return promise.then(handler);
}),
catch: curry((handler, promise) => {
return promise.catch(handler);
}),
};
let x$ = pipe(
M.then(x => x * 2),
M.then(x => x * 2),
M.then(x => x * 2),
M.catch(err => console.error(err))
)(Promise.reject(new Error("oh no!"))); I'm also interested how you solve laziness mismatch. Rx Observables are lazy and Promises are not. |
I'd love to have all the statics functions curried as well ! ✨ |
@jgoux Cool. We are going to curry everything in the new a la carte packages, and |
Could you please provide an example of point-free code with most.js? |
@dypsilon Just to be clear: most.js core (ie.. Here are a few examples of doing pointfree with most.js (if you use a curry helper): import { curry2, compose } from `@most/prelude`
import { map, filter, from } from `most`
const add1 = x => x + 1
const even = x => x % 2 === 0
// Imagine map and filter are curried
const mapc = curry2(map)
const filterc = curry2(filter)
// pointfree declaration of a function that adds 1 to every item in a stream
// and another that keeps only even-valued events
const add1s = mapc(add1)
const evens = filterc(even)
// compose (right to left), also pointfree
// add1Evens is now a function that takes a stream,
// keeps the even-valued events, and adds 1 to them
const add1Evens = compose(add1s, evens)
const numbers = from([1,2,3,4])
add1Evens(numbers).observe(x => console.log(x)) //=> 3, 5 Of course, the real benefit of currying and composition is that once partially-applied and composed, those functions can be reused over and over. |
Thank you for this lengthy example. My initial though was, that it's already possible with native most tooling, I just don't know how. Currying the functions myself works fine for me, but I definitely think autocurrying would improve most for this style of programming. Off course it's important to test if this feature will handicap performance. Here is a tiny hint for fellow pointfree programmers: you could just use Ramda functions like map and filter directly on the stream, since they dispatch to stream.map(x) internally. This way you avoid currying for most functions. |
No problem @dypsilon. Auto currying may happen after 1.0.0 (which is very close, see the roadmap).
Yes! Thanks for mentioning this. Ramda + most.js is quite a convenient setup since most.js implements a good bit of fantasyland. |
@briancavalier those are some good news. Cudos to the team behind most.js. I worked with RxJS, Highland and node.js Streams in object mode. Most.js is the easiest to grasp and work with, while providing some power features like monadic composition, promise composition and now pointfree style! |
@dypsilon Take a look at the following benchmarks — Pretty much no change in performance. I think this is going to be an awesome addition to mostjs. I used const [
mReduce,
mMap,
mFilter,
mFrom
] = [
R.curry(most.reduce),
R.curry(most.map),
R.curry(most.filter),
R.curry(most.from)
]
const fileMapReduce = R.compose(
mReduce(sum, 0),
mMap(add1),
mFilter(even),
mFrom
) .add('most-curried', function (deferred) {
runners.runMost(deferred, fileMapReduce(a));
}, options) My concern is how big the library would get if we simply import an external curry function. |
@tusharmath We actually already have curry defined in |
@TylorS I went thru the code for currying there — https://github.com/mostjs/prelude/blob/master/src/function.js#L13 it looks like it essentially hard coded for functions or arity 2 & 3 only. How about a more generic approach — function Curry(f) {
return function curried(...t) {
if (t.length === 0) return curried;
if (t.length === f.length) return f(...t);
return curried.bind(this, ...t);
};
} |
@tusharmath I think thats a interesting approach but why do you really need a curry for more than 3 arity? majority of the static method in this lib are binary and some have an arity 3 but i feel like going over 3 would be a code smell, no? plus with es6 if you want to take a "variadic" approach you can do thoughts? |
Yeah agree it would be. but the code above is actually generic+concise enough for arity = 1, 2, 3 also. That's all :) |
i see what you mean @tusharmath was just curious on your take |
The reason for the hard coding is performance. We avoid the need to use Also, there are no parts of the public API which take more than 3 arguments (currently), which is why we only wrote the curry2 and curry3 functions to begin with. |
yeah @TylorS that was my next point i would think the engines would prefer to deal with fixed or known arities vs ones that are decided at run time based on the arguments passed to the function. though i wasn't sure exactly on the performance implications. |
Totally true! But have you considered that in a real world use case these functions are going to be polymorphic and would anyways get de-optimized. |
FWIW, |
@jgrund Yeah, that's good progress. It's only v8, but hopefully other engine will follow suit. @tusharmath your curry function is quite nice 👍 Once VMs optimize both |
@briancavalier I would suggest How can I help mostjs in this regards? |
We have to be careful not to leave other VMs in a bad spot. Does anyone have any data on
Thanks for the offer! I think if we get data on other VMs, and we see that |
Currently, most provides both prototype and "static" versions of each combinator. The static versions should be curried to make them nicely composable. We can curry at the public API boundary (ie in the main most.js module) to avoid using curried functions internally.
The text was updated successfully, but these errors were encountered: