Trine – A utility library for functional programming in JavaScript
github.com
github.com
It's one thing to have to keep on top of the new frameworks springing up every five minutes - which you can, for the most part, ignore for at least six months until they start to gain some traction or have disappeared entirely - but when the core language itself is changing (and growing) at this rate, it really becomes an altogether more disconcerting and nightmarish proposition.
Most of it is just superfluous fluff that doesn't actually do anything but change the syntax to something they perceive as fundamentally better, when it's just a matter of taste and fashion.
Let's say I make a new language called javascript++.
Instead of having `function` be a keyword, I chose `functioooooooooooooooooooooooooooon`. Based on their syntaxes, do you find one fundamentally better?
Off the top of my head, other languages that are considering and implementing syntax extensions include Python (e.g. "yield from"), C# (e.g. "async/await"), and Java (lambda expressions).
I actually think that it's pretty low to accuse ECMAScript of "mangling" JS syntax when they are just trying to add modern extensions that make common patterns easier. Of course this function bind syntax is just a proposal and hasn't been accepted into the standard yet. But what about it bothers you so much? What about iterators or shorter function definitions, are those also nightmarish manglings of syntax? I mean, really. The usage of Javascript is evolving from simple embedded scripts to gigantic client/server applications. It's no surprise that the language itself will evolve as well.
items .filter(isOk) .map(toOtherType) ::flatten()
In C# that sort of case is primarily handled by extension methods. They have their issues not least in terms of collisions, but to me they are a beautiful solution. The equivalent in JS would seem to be the always unpopular choice of adding these methods to the prototype, and as with extension methods you'd want the user to opt-in to their addition (possibly at object level here?).
Not sure though, maybe I've missed something.
If you took the function bind syntax to its natural extreme, you end up with a model like Python or Nim, where all instance methods accept their "this" as a first parameter, and "a.call(b)" is equivalent to "call(a, b)". Personally I always thought that was a particularly elegant way of implementing instance methods, because it doesn't require inheritance or modifying the original type.
In Nim it even lets you do some very interesting method chaining to replace nested calls, like "toInt(sqrt(toFloat(num)))" can be equivalently written "num.toFloat.sqrt.toInt"
Not sure on the whole this as argument, I prefer it to the current approach of this being decided at framework/library level because I get bored of having to bind each function to get lexical scoping back. That Nim code is indeed beautiful though, maybe need to give ti a look ta.
I think the uncertainty around Javascript (and I do agree that there seems to be a real sense of confusion in the community when you look at the explosion of libraries and frameworks) is more related to the fact that we are trying to solve hard problems in a language that really gives us no help. We get no type checking, no good concurrency APIs, no immutability, no good sequence abstractions until iterators, just a fairly low-level and extremely hackable language with an unusual prototypal inheritance system.
Well, who knows. I would personally like to see some more stuff be standardized in the language or the official APIs so we don't end up with four competing libraries implementing "map" and "reduce", or promises, or whatever other thing.
The only difference with ES6+ is that the uncertainty will come from the addition of features people have complained were missing, instead of the traditional uncertainty that came from the intersection of its flexibility and dev's desire not to learn it.
I feel that hasn't changed over the years outside of more and more people not knowing how to use JavaScript (and more and more people who actually know their script).
function Person(){
this.age = 0;
setInterval(() => {
this.age++;
}, 1000);
}
and function Person(){
this.age = 0;
var my = this;
setInterval(function(){
my.age++;
}, 1000);
}
are barely visible, but the second one is actually legible. Why are we arrowing in nothing into curly braces? Oh, it's actually a function. Great. foo.bar()
then inside bar(), "this" is foo. Whereas if you do: quux(function() { console.log(this); })
...the "this" will point to whatever quux's "this" is, and it could be anything, depending on what quux() is doing. You can't depend on "this" being correct here.Hence people have for years used bind():
quux(function() { console.log(this); }.bind(this));
But this is neither nice to read (or write), nor is it performant. Many libraries, such as Underscore, also allow you to pass in a context variable: quux(function() { console.log(this); }, this);
This requires that quux() passes the context as the "this" argument to the function.The lambda (arrow) syntax fixes all of this [pun] by preserving "this" as a lexically scoped reference:
quux(() => console.log(this)); let names = persons.map(p => p.name);
Compared to ES5: var names = persons.map(function(p) { return p.name; });
The arrow syntaxes increases readability pretty much everywhere. Promises, for example: save(value)
.then(result => this.update(result))
.catch(err => Application.showError(err, "Could not save."))
(The use of a function in the "then" here is to avoid having to bind() the function; the alternative would be: .then(this.update.bind(this)).)Your setInterval example is a somewhat inappropriate example of the usefulness of arrows as it doesn't take any arguments and doesn't return anything (so you didn't need the braces).
But the fact that you have to alias "this" is a big argument in favour of arrow syntax. It may be trivial in a small example, but it's not trivial when extended to an entire app. You'll pretty much end up aliasing "this" in every single method. If you change any logic around, you'll end up having to either add new aliasing, or chase down unused alias variables, just to add/remove closures. (And what do you call that variable? Is it "this_" or "_this" or "self"? When working on a team you'll have to agree on a convention if the code is to remain readable.)
And your second example; I was going to re-write it, but then I realized I couldn't because I didn't know what `this` was. I don't think `this` is useful, and I try to avoid it because of its malleability and the confusion it often brings because of its overly dynamic nature.
[1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe... , Ctrl-F for `Person` or something
Not sure why you don't know what "this" is in the second example. The whole point is that it's predictable. "this" is always the instance your method is defined in, unless you bind it to something else, which requires being explicit. I intentionally didn't include any context, but think about it this way:
class Store {
save(object) {
this.client.put("/objects", JSON.stringify(object))
.then(result => this.update(result))
.catch(err => Application.showError(err, "Could not save.")) }
}
}
"this" is extremely useful. I'm not sure how you could argue otherwise. Here's another pattern I use all the time: this._socket = new WebSocket(`ws://${url}`);
this._socket.onopen = () => {
this._state = 'connected';
this.emit('connected');
};
The lambda syntax allows placing logic lexically where it belongs.You don't know how much you're missing here. I wish that ECMAScript* considers adding support for "indented syntax" like in Sass to make it even more easier to read and go through the codebase without seeing all these ugly braces around.
*: or a white knight programmer volunteering to offer this functionality for us all :)
I just want the indentation without all the baggage that comes along with CoffeeScript.
function Person() {
this.age = 0;
setInterval(this.incAge, 1000, this);
}
Person.prototype.incAge = function personIncAge(that) {
that.age += 1;
};
http://jsperf.com/bind-vs-closure-vs-paramThey're not expensive. You're not binding thousands of times a second anyway, so stop your silly premature optimizations.
2. You are making an assumption that you aren't binding thousands of times a second. You need to take actual use case into consideration.
3. My example has a 1000:1 method call to bind ratio and it's still a significant difference in benchmarks. Yes, it's that expensive it's important to think about in critical sections of code.
However, in my opinion, repeating this many many times throughout a codebase is irritating to write, and adds extra weight and bug surface area to the code.
Therefore adding some syntax sugar to remove the need to do this, i.e. the first example, is a nice feature. I know opinions on syntax and formatting are pointless and endless, but could you please explain why, for you, is it less legible? Once you're used to it, don't you just scan () => as no-arg anon function signature the same way you scan function() as that now?
> ...can also represent infinite sets
This is the highlight.
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?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.
var process = items => items.filter(isOk).map(toOtherType).flatten() // process1 :: (Item -> Bool) -> [Item] -> [OtherType]
// process2 :: [Item] -> [OtherType]
process1(isOk, items); //=> [otherType1, otherType2, ...]
process2(items); //=> [otherType1, otherType2, ...]
var processR1 = seq(filter, map(toOtherType), flatten);
var processR2 = processR1(isOk);
var processT1 = (isOk, items) => items.filter(isOk).map(toOtherType).flatten()
var processT2 = items => processT1(isOk, items)
That's why I'm wondering if there's some easier way to do this. var processT1 = (isOk, items) => items.filter(isOk).map(toOtherType).flatten();
var processT2 = partial(processT1, isOk);
http://benalman.com/news/2012/09/partial-application-in-java... > 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.The best answer to this seems to be simply:
var sum = ls => ls.reduce(add, 0)
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. sum = reduce::partial(add, 0);
[1,10,20]::sum();That's exactly where I got caught too. I just don't see why I really want to do it that way at all.
My favorite thing about Underscore/Lodash/Ramda is function composition. This seems like a (syntactical) move away from that . . .
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.
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!
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: let getIds = -> this::map(-> this.id);
> Best of luck!Thank you, and likewise! <3
function process () {
return this
::filter(isOk)
::map(toOtherType)
::flatten();
}
and then later: groupsOfItems::map(process);
Granted, the `flatten` function is not in the Trine yet, somehow forgot that from the initial release.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.... said the person not understanding the language semantics.
Passing arbitrary data as this is a terrible idea. It has to be an object and as such all primitives end up boxed.
I didn't have to box anything to pass a primitive as this. Maybe some other versions are different, but this works in Chrome 40 and Firefox 33.
> function isboxed() { return this instanceof Number; }
> isboxed.call(5);
true > var isboxed = (function() {"use strict"; return function isboxed() {return this instanceof Number;}}());
> isboxed.call(5);
falseThat being said, the readme did not address why Ramda uses the data as the last parameter and not as `this`: To facilitate partial application.
However, I agree, the readme doesn't quite show the full power of Trine. Trine has `partial`, which allows you to do partial application as so: `parseInt::partial(_)`, that would create a function that acts as parseInt but only takes one argument. This is in sync with the partial application syntax proposal: https://gist.github.com/anonymous/5c4f6ea07ad3017d61be
The rest of the functional goodness, such as autocurrying are still undecided upon because in my personal experience autocurrying in JS is awkward and leads to bugs that are hard to debug. I'm also considering compose and a combination of compose and partial (e.g. apply a function to a parameter at a certain place), but again subjectively, composing functions like that has reduced the readability of codebases. JS already has first class functions which allows composition pretty easily, the only problem is that it's overtly verbose - I'm still hoping to see the single arrow function land in JS some day. :)
Do you mean thin arrow fn(s)?
Of course, I want to run my server in the same way.
So perhaps this is better done inside some "deterministic virtual machine", where the browser and server are run deterministically hand-in-hand.
Second, you can actually execute browser and node code deterministically if you like, by providing alternative implementations of setTimeout, setInterval, setImmediate, etc. that serialize execution of the callbacks in the order of your choice.
IMHO it's usually something to be avoided, not championed...
Fucking lol, why?
I guess the Lodash/Underscore definition of "functional" was right after all!
</sarc>
...I'ma go back to using Ramda now.
Not sure I get why installing `babel-runtime` would be something you'd have as a separate install step though, since there is no guarantee that the user would install the right version.
It also means trine can never upgrade the version of Babel it is using internally, since Babel could change the helper name that it uses or something, but that might not exist in the version of babel-runtime being used in the code that depends on trine.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
But if people are concerned about syntax mangling now, those features are going to make things 100 times crazier.
One of your iterator example for primes uses GOTO (aka labels).
It may be different where you work, but anyone in my company using labels either has the mother of all excuses or is going to be a warning away from termination.
Just as no one is suggesting Rust the game and Rust the programming language are violating each others mark...this Trine doesn't violate other "Trine" marks. Trademark is protection for a specific mark in a specific industry, and not the exclusive right to use a word for any purpose.
Made up words are sometimes more strongly defensible... Thus "Google" would not be usable legally in the US for any product. But "googol" might be for products that don't overlap with products made by Google. But, Trine is not a made up word.
Also, I don't see how "let's not make a thing better just because it was not meant to work this way" is an argument.
I'm sure there is a fallacy name for this, but consider some other examples: "You could always use fridges for cooling and let cars be" or "You could always use computers for browsing the internet and leave phones be".
You could technically use some FPLang-to-JS compiler,
but those suck more often than not.
Some citation needed on that one. I've got a whole host of contradictory examples. Do you mean to say that you personally have not fancied any of them so far? If someone has done some deeper digging into the state of compiled vs. handwritten JS I'd be delighted to read about it though.Features like the function bind syntax are only possible with Babel or another transpiler. It will likely be years before you can use them practically without a transpiler in the browser.
A lot of the new syntax/macros/APIs/etc should just be built as modules or Babel plugins rather than core language features. Just because [Syntax X] improves a couple of code snippets doesn't mean it needs to be shipped with the next version of JavaScript.
I like this idea a lot. Mostly because it would keep JavaScript from becoming the huge syntactical and idiomatic minefield that modern C++ is.
Sure, most JavaScript code is imperative, but there's no particular reason it should be. Functional programming isn't some late addition, either: the language's design was influenced by functional languages alongside imperative ones.