
Trine – A utility library for functional programming in JavaScript - quinnirill
https://github.com/jussi-kalliokoski/trine
======
bshimmin
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.

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

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

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

------
CrossEye
(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?

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

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

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

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

~~~
implicit
Are you sure?

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

~~~
CrossEye
as inglor said, this depends on strict mode:

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

------
Kiro
What's up with the snarky slogan?

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

~~~
quinnirill
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](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. :)

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

Do you mean thin arrow fn(s)?

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

~~~
notNow
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?

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

~~~
notNow
Thanks for the explanation.

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

~~~
jpeterson
What's stopping you from doing that, exactly?

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

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

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

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

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

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

Fucking lol, why?

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

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

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

~~~
jdd
Yep. There's a package with the method transformations already applied –
[https://www.npmjs.com/package/lodash-
fp](https://www.npmjs.com/package/lodash-fp)

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

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

~~~
quinnirill
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". :/

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

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

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

------
romanovcode
Looks like some kind of LINQ implementation for JS.

~~~
innguest
Also known as the List monad.

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

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

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

~~~
brunoc
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...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/String/includes)

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

~~~
quinnirill
Sounds like a very healthy company culture. ;)

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

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

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

~~~
gabipurcaru
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".

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

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

