(Disclosure: one of the authors of Ramda here, so I may well be biased toward a very different approach.)
This is an interesting approach, but I get caught right at the beginning, where you say "What you'd really want to do is this:
items
.filter(isOk)
.map(toOtherType)
.flatten()
But what I really want to do is build a reusable function:
var process = seq(filter(isOk), map(toOtherType), flatten);
So I can at my leisure call
process(items);
Or later put `process` in another sequence, or do something like:
map(process, groupsOfItems)
And I don't see that Trine helps with that. Am I missing the way to use Trine to build functions, or do I simply have to wrap up a Trine construct in a function that takes a parameter, does its Trine magic, and returns that result?
I believe you showed in one of your demonstrations (or someone else showing ramda), a very simple elegant example:
> sum = reduce(add, 0)
> sum([1,10,20])
With something like trine, composition can only happen really happen at usage-time, while with Ramda it allows you to reason about the operations separate from the data. I suppose trine could achieve the same by further complicating with some sort of dummy data:
sum = __placeholder__.reduce(add, 0);
[1,10,20].::sum()
But this gets difficult because __placeholder__.reduce itself needs to return something that can be chained (certainly doable to return a function that is also a dummy object I suppose?). But anyways, to me this starts looking more and more complex, and making it harder to reason about constructing (composing) NEW functions from existing ones.
ISTM that this is neither as elegant nor as easy to reason about, but it's also not horrible. It really moves away from the easy association that Ramda makes between
var currentTotal = reduce(add, 0, nbrs);
and
var sum = reduce(add, 0); //=> :: [Number] -> Number
Of course this arrow syntax is ES6 (or transpiler) only, but the library is predicated on that notion. Ramda is a bit of an oddity, still maintaining compatibility with ES3 - ES6.
(Disclosure, Trine author here, and also admittedly very inspired by Ramda.)
I actually used Ramda quite extensively a while back, because I was convinced it was the right way. However, a while after looking at stack traces that lead to Ramda and not even myself understanding my where each parameter in my own code goes, I decided to stop using it and also dropped these features from Trine. Trine has partial, and that's as far as magic goes, in fact I wanted to make it as far from magical as possible. I think plain old functions are the best form of function composition, albeit I'd love for the syntax to be terser. They're easy to read and easy to reason about, also easy to reorder, no need for higher order functions that go in the middle.
It's a funny world. Each of us is looking at the other library thinking there's too much magic involved. :-)
Ramda is [considering a technique][1] that would significantly reduce call stacks. But there is nothing likely to help with you understand parameter orders of your functions. Many users annotate their functions with something like Haskell signatures.
Thank you for bringing forth another interesting library. I'll be following your progress. Best of luck!
> It's a funny world. Each of us is looking at the other library thinking there's too much magic involved. :-)
Heh - I think what feels like magic is the syntax that Trine is presuming, which admittedly might seem magical before trying it out. :) The library itself is just a collection of very primitive methods (with the exception of partial()).
> Ramda is [considering a technique][1] that would significantly reduce call stacks.
That's good to hear! But what I'd really like to see is something that would make the stack trace have a reference to the function definition site, e.g. if I have an error like
var getIds = map(prop("id"));
var ids = getIds([{ id: 1 }, null, { id: 2 }]);
the stack trace would also show the definition site of getIds. This is what I get when I compose functions just using the vanilla JS syntax:
function getIds (items) {
return items.map(function (item) {
return item.id; // the stack trace will point here, and I can also add a breakpoint here without it stopping on every unrelated prop() call
});
}
Like I mentioned on several occasions here, I'm hoping for JS to get simpler unbound function syntax, which would work well with Trine, and still have the aforementioned benefits:
It was finding that those tools were no longer supporting the way I wanted to work that got me started on Ramda. I'm guessing the same is true for the author of Trine.
Why not both? lodash is modular so you can use the bits you need. There's also a flavor with auto-curried iteratee-first/data-last methods too https://www.npmjs.com/package/lodash-fp
Ok, that helps make it a bit more palatable to me.
Thanks for the information.
One question, though: How do user functions interact in here? If you didn't include `flatten`, but I had my own version of it, would it be straightforward for me to build `process` using my own `flatten`? Do I have to modify some Trine objects, or can I use some simple function references?
Obviously I haven't yet actually dug into the Trine code. Perhaps this weekend.
Yes, you can use just simple function references, e.g. you could just define flatten as so
function * flatten () {
for ( const item of this ) {
yield * item;
}
}
And it would work just like it was part of Trine - in fact that's probably how it's going to be implemented in Trine. The chaining is not a feature of Trine, but the function bind syntax proposal ( https://github.com/zenparsing/es-function-bind ), Trine has merely been designed to work well with this syntax.
Very well said. I work on the Ramda (http://ramdajs.com/) FP JS library with the OP. Ramda is heavily invested in currying, and I don't yet have a clue how we'll deal with default parameters. At first guess, we'll simply have to explain that we can't curry such functions. Such a shame!
People are going to be told (rightly so) to use `let` instead of `var`. If they do so too mechanically, and their code is structured in certain ways, an expression which could never before throw an exception in Javascript, `typeof x`, now will do so.
This is not the end of the world, by any means. And it doesn't add an exception into unchanged code, but it still is a legitimate concern.