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?
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. 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.)
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?
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-paramYou 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 :)
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.
> ...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? > 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!
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.
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.
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.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.
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.
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.
That 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)?
... 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);
trueNot 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.
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.
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.