Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Trine – A utility library for functional programming in JavaScript (github.com/jussi-kalliokoski)
159 points by quinnirill on June 11, 2015 | hide | past | favorite | 111 comments


About halfway through the README, after some examples of the new bind syntax, it says "But why stop there?" Actually, please can we stop here? Just for a bit? This continual, persistent mangling of JavaScript's syntax really is starting to hurt.

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.


This isn't the core language changing though. This is people who are new to javascript coming to it and deciding they all must write their "hello world" framework / extension.

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.


I'm not sure it's purely 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?


Well clearly "function" is "better", because it's less prone to typos, easier to read, and makes more sense. But if you can make a good argument to go with "functioooooooooooooooooooooooooooon" then go for it.


I was almost afraid no one else thought this way. This just makes JavaScript illegible, and adds a negligible number of new features. The only JS I'm ever going to write is ES5, at this point, because the difference between

  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.


A better example, in my opinion:

    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.)


MDN thought it was a good example... [1]

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


That MDN page gives a good example of how it works, but it's not intended to convince doubters with persuasive arguments.

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.


I haven't touched JavaScript in years, so I'm curious about one detail: why does the second snippet need you to alias "this" to "my" and the first doesn't? If it wasn't for that, it would look like mere syntactic sugar for lambdas, but this difference makes me think there's something more to it.


Function calls bind "this" to the caller. So if you do:

  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));


It is a feature of the new syntax to bind the current context (what "this" points to) to the created function.

http://tc39wiki.calculist.org/es6/arrow-functions/


It actually is required, because `this` is mangled whenever you create a function. If I added .bind(this) to the function, it would also work, but I alias the object instead. Yes, it's confusing, but the fact that there's a difference is even more confusing.


"this" inside the setInterval in the 2nd example points to the global object in the browser case it's the window. So, he had to cache/alias the value prior to execution to avoid the confusion.


I agree that the second example, using (var my = this) and then using my inside the anonymous function is not much of a hassle.

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?


I agree, but closures and bine are expensive--present in both cases. It's better to avoid it. Granted, this is a contrived example and sometimes closures are the more elegant solution.

  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-param


Ugh — this plague again. No other coding community has latched on to performance tests like the javascript one. For NO GOOD REASON.

They're not expensive. You're not binding thousands of times a second anyway, so stop your silly premature optimizations.


1. I already gave an out for your point. Yes, always weigh performance vs. clarity and don't over optimize.

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.


Meh; rockstar developers with no CS background working for over valuated MVP companies implementing half understood techniques described in shady Haskell tutorials cannot possibly be wrong.


Well, the no CS background and shady Haskell tutorials parts were correct; My highest formal education is high school. ;) But this was done on my free time, and I don't really identify myself as a rockstar developer. Sounds pretty arrogant to me.


Do you know that the arrow function was the main reason that I made the switch from Chrome to FF Dev as my primary web browser and I never looked back?

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 :)


That's CoffeeScript right there for you.


I'm familiar with CoffeeScript but AFAIK it's a distinct language probably a dialect of JS.

I just want the indentation without all the baggage that comes along with CoffeeScript.


I disagree. I think that extending and improving a language is good, and it's certainly not a Javascript-only phenomenon.

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.


My objections are around the rate of change and the fact that JavaScript is growing into a large, complex language that people are becoming really uncertain how to use.


I could see how that's a legitimate concern, it reminds me of some of the complaints I hear about C++ having too many features (I don't use C++, so this is just hearsay). But Javascript is still a much smaller and simpler language than almost anything else out there. Sure, it has some semantic quirks, but the syntax is fundamentally minimal. I think it will be a long time yet before we reach the point of being really overcomplicated.

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.


So interesting thought here...but C# might be a better comparison. This is a language that has seen a lot of change and a lot of added features. But at least some of the earlier features that were part of the language are no longer used much. I think js is more like C# than C++ in this way.


JavaScript has always been a language people were uncertain how to use.

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.


No, in fact the real problem has been that too many incompetent people were far too certain about how to use it.


The selling point of JavaScript to me, 12 years ago, was "You don't need to know JavaScript to use JavaScript" and most of the people I saw using it were uncertain of how to actually use it (most of those people also being 13-17 in the crowd of web-design forums I hung around with).

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).


You are the only person who is really uncertain on it.


You have a point but you mention C#, it's worth thinking of how you'd handle one of the examples in C#:

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.


I think extension methods in C# are basically a way to work around the fact that you can't just stick a method on the prototype like you can in Javascript. I also like the C# solution, actually, I just love C#. It's a great "daily driver" language in my book.

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"


Yup agreed but the prototype approach is scarier as it affects every user of the prototype, where as with namespaces you opt-in to bringing in extension methods at the file level by importing the associated namespace.

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.


Oh you missed out so much by stopping at "But why stop there?"

> ...can also represent infinite sets

This is the highlight.


(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.


That is ceratainly one of my standard examples.

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.


Trine can actually already do this particular example:

    sum = reduce::partial(add, 0);
    [1,10,20]::sum();


(Disclosure: big fan of Ramda here)

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 . . .


(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!

[1]: https://github.com/ramda/ramda/pull/907


> 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:

    let getIds = -> this::map(-> this.id);
> Best of luck!

Thank you, and likewise! <3


Agreed. And btw thank you for Ramda, which is a very beautiful thing. Too sad in day-to-day web development lodash is just enough.


That's not sad. Use the tools that work for you.

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.


and thanks again for Ramda .. I use it daily for real work, back and front end.


If you mean using frameworks to display data, then yes. For anything substantial I'd use ramda over lodash.


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


With Trine this would be

    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.


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.


Happy to help!

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.


Seems like you could just do

  var process = items => items.filter(isOk).map(toOtherType).flatten()


The difference starts to show when you want to partially apply some values.

    // 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.


I believe you can use partial() from this post[1] to do something like 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...


> They're both (subjectively) wrong: the natural place for data in JS is the this parameter.

... 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.


Not in strict mode... `(function(){ console.log(typeof this); })(5);` try in sloppy and strict mode - remember modules are strict mode by default.


(function double() { return this * 2; }).call(2) === 4

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.


Are you sure?

    > function isboxed() { return this instanceof Number; }
    > isboxed.call(5);
    true


as inglor said, this depends on strict mode:

    > var isboxed = (function() {"use strict"; return function isboxed() {return this instanceof Number;}}());
    > isboxed.call(5);
    false


What's up with the snarky slogan?


Looks interesting, especially in concert with the proposed bind syntax. Fortunately, that syntax is in Babel now, so we can play with these ideas straight away.

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.


Ramda actually uses data as the last parameter usually, whereas lodash has it as the first.

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. :)


Whoops, my mistake, I meant to say last parameter, which is exactly its point. I recall discussing this with the authors at length, because allong.es usues a similar strategy with functions named ___With, e.g. mapWith, filterWith.


lodash-fp is a thin layer over lodash enabling auto-curried, data last methods – https://www.npmjs.com/package/lodash-fp


May I ask what do you mean by "single arrow function"?

Do you mean thin arrow fn(s)?


Yes, sorry, thin arrow, not single - `() -> this.foo()` for function expressions and possibly even `let/const fn () -> this.foo()` for declarations.


Excuse my ignorance but I only have experience in JS and not any other "advanced" or well-established programming langs, how's thin arrow syntax gonna fix this prob that fat arrow can't?


Fat arrow functions have lexical this: this inside the function is bound to what it was outside. Thin arrow functions have a dynamic this, determined by how they are called. Because the library relies on dynamic this as the place where the data is sent, fat arrow syntax isn't useful with it.


Thanks for the explanation.


What Javascript needs is a way to deterministically (re)run it, so that errors can be traced.


What's stopping you from doing that, exactly?


He's probably having issues with async gubbins. Some sort of 'rewind time' in the debugger so you could determine what happened when to get to this state of error.


The best vendor-neutral term for rewinding time in a debugger is probably "historical debugging" or "reverse debugging".


I also want to be able to run javascript (and the rest of the browser) deterministically in a test environment, and run tests, each with different timing characteristics.

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.


First of all, the browser environment is the asynchronous part here, not JavaScript. JavaScript is a language that happens to be interpreted on top of that environment. You'd run into similar issues with any other language over an asynchronous execution model.

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.


It seems to me like everybody wants a framework in every language that allows them to write it like a language they're more comfortable with. I would much prefer to see the evolution of FFIs, and things like that, than just writing a DSL that's more or less just sugar. Let's embrace the native strengths of languages, and allow ourselves to use more languages together.


This, and eval are probably the two largest points of JS where unintended mutations and consequences happen. Also, by overloading this (which is something I always hated in jQuery), you lose the ability to compose functions together, and/or use other types of currying and binding for event handling and/or processing.

IMHO it's usually something to be avoided, not championed...


> The library modern JavaScript doesn't deserve, but needs right now.

Fucking lol, why?


It's a shame there isn't any cross platform supported alternative to mangling javascript even further. But getting all browser developers to agree is probably an even worse nightmare.


Gosh, and here I thought functional programming was about composing higher-order functions independent of the data on which they operate.

I guess the Lodash/Underscore definition of "functional" was right after all!

</sarc>

...I'ma go back to using Ramda now.


I recall lodash author saying that if you want HOF, Datum order to change you can `rearg` the whole library to suit your needs. Maybe the performance penalty is too large, I never tried it.


Yep. There's a package with the method transformations already applied – https://www.npmjs.com/package/lodash-fp


Ha, so it's compile time, problem solved. (Thanks)


Looks cool.

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.


This is to avoid multiple versions of Trine required by libraries (or the application itself requiring a different version of babel-runtime) causing the different versions of `babel-runtime` to be included multiple times, which would be a problem especially for browser applications. Trine itself is very small, but if each module introduces a dependency to babel-runtime, it would be another case of "npm downloads the whole internet". :/


To me at least, that's more up to NPM to manage though. As it is, it means you're essentially sidestepping npm. There's also no guarantee that people will install a version of babel-runtime that is compatible with the version of Babel that you used to publish the code.

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 downside of passing data as `this` that isn't mentioned is you can't use the concise function syntax `=>`. If we ever get thin arrows this problem will go away.


There's no point of using `=>` arrows if you're using the bind syntax they're mutually exclusive.


Right on the point. I'm still hoping for that to get in at some point. :)


Looks like some kind of LINQ implementation for JS.


Also known as the List monad.


Perhaps what programmers of modern JavaScript should be doing is going back and revisiting things that have been considered harmful practices for quite a while. Some work in extending prototypes would make a huge difference to the readability of lots of code - yet everyone is told this is a bad practice because extending DOM elements is problematic and people are lazy when it comes to global namespace pollution. Realistically, neither of those are reasons for it to be bad practice anymore.


Extending prototypes seems like a great idea, until other code running in the same environment does it, and you realise why people stopped doing that.


And also because it has prevented the language itself from standardizing some features, e.g. String.prototype.contains had to be renamed to includes because shipping it as contains would have broken sites that use mootools.


For those of you like me who spat their coffee reading this, you can find additional information about this anecdote here.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...


You're obviously right but could this sort of case not be handled by future versions of JS allow clients to opt in to the prototype being modified, something equivalent to extension methods in .NET where if a client wants to use them they import the associated namespace?


?Namespaces?


[deleted]


Especially under Node.js prototype modification can be a very big issue. Because once you start any third party module, all bets about knowledge and control of your environment are off. Someone, somewhere down the module chain might have messed with the prototype chain in some way and you won't know it until you run into the resulting bugs.

I've been there, and once you start to see random debug logs popping up in your console because you somehow ended up iterating over a `enumerable` prototype extension in a string "enhancement" library used by a third party module, that is used by a third party module. You'll end up having to take one of three choices: Either modify your code to deal with the mess, contact the author of the string library to fix it (and all upstream authors to update their dependencies) or just drop the top level library altogether. None of these are perfectly clean / easy and in some cases these might they might also be impossible (author cannot be reached, won't change his code, you code can't handle it or you simply cannot drop the third party dependency).

Once you start writing a library, you got quite a bit of responsibility resting on your shoulder to not break things upstream :)

We already averted the "nightmare" of io.js / node.js ending up with different ES6 feature sets, so we shouldn't open a new can of worms.

The Node.js ecosystem is wonderful and that in big part stems from the fact that you can basically install any module at any time and things just work.


> If you've got a fair degree of knowledge and control over what's in your environment (e.g. Things are documented or standardised, you're using node.js), I don't really see the issue.

Are you checking every single library you use for prototype changes, including the libraries each library depends on, the libraries those depend on, etc.?


Another thing that may change this in the future is proxies and "method not found" type of functionality in JS.

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.


WTF!!

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.


Sounds like a very healthy company culture. ;)


I hope that the authors have cleared the name with FrozenByte studios ...


Trademark doesn't work the way you think it does.

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.


You could always use FP languages for FP and leave JS be...


Well you can't really. If you want to write frontend apps, for example, you have to use JS. You could technically use some FPLang-to-JS compiler, but those suck more often than not.

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.


I'm using on my day to day ClojureScript and have, till now, 0 technical issue. Is this the perfect platform ? Hell no, it can be annoying to set, and interop can be annoying (mostly due to the fact that the world of side effect is still there, lurking and come at odds to my immutable language). The upside is that it works. For me it's a relief to use a FP compare to OO, some would prefer a logic (prolog) approach, or else - and again, we can, right now and it's no harder than using grunt, bower or any of the other tool that have appeared recently.


> You could technically use some FPLang-to-JS compiler, but those suck more often than not.

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.


> A lot of the new syntax/macros/APIs/etc should just be built as modules or Babel plugins rather than core language features.

I like this idea a lot. Mostly because it would keep JavaScript from becoming the huge syntactical and idiomatic minefield that modern C++ is.


This presupposes that JavaScript is not a "functional programming language". But that's not really true. JavaScript is a multi-paradigm language supporting both imperative and functional styles.

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.


I thought JS was intended to be a Scheme in disguise?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: