
What’s New in ES2019 - davman
https://blog.tildeloop.com/posts/javascript-what%E2%80%99s-new-in-es2019
======
mmartinson
Honest question, not meant to be inflammatory.

If we still need to target es5 4 years later, and transpilation is standard
practice, why bother? Is the evolution of JS not directed in practice by the
authors of Babel and Typescript? If no one can confidently ship this stuff for
years after, what’s the incentive to even bother thinking about what is
official vs a Babel supported proposal.

I like the idea of idiomatic JS with powerful modern features, but in practice
every project I’ve seen seems to use a pretty arbitrary subset of the
language, with different ideas about best practices and what the good parts
are.

~~~
simias
That's not unique to JS. Some compilers took over a decade to implement C99
features. Most of us C coders were still defaulting to C89 for portability
well into the late 00's. But now C99 is mainstream enough that you can
(mostly) safely target it.

If you never release new standards you'll never be able to use them. I realize
that in JS world 4 years is basically an eternity so it's hard to project that
far but I'm sure that if you still have to write javascript code 10 years from
now you'll be happy to be able to use the features of ES2019.

Regarding transpilation surely it's not as standard as you make it out to be?
It's popular to be sure but handwritten javascript is not that rare nowadays,
is it?

~~~
pault
> It's popular to be sure but handwritten javascript is not that rare
> nowadays, is it?

Long time front end developer here. In the last couple of years I can't recall
seeing even a single project without a build pipeline (not that they don't
exist, I just haven't encountered them at my day job, first or third party).

~~~
baddox
I don't understand what is so appealing about not having a build pipeline for
JavaScript, other than the ability to very quickly test and learn things
directly in the browser, which of course anyone can still do.

For anything remotely important, you're almost certainly going to already want
a build pipeline to do things like concatenating/minifying code, running
tests, and deploying. Adding a transpilation step to extend support to older
browsers comes with almost no cost to time or maintainability, assuming you're
using well-supported things like Babel and its official plugins.

~~~
SahAssar
The appealing thing about not having a build pipeline is not having it. Not
having another thing to maintain and update/upgrade/debug. I've gone through
grunt, gulp, webpack and parcel. Somewhere along the way I realized that with
es6 imports and css variables I don't really need it.

I've talked with frontend devs who only worked on projects with build steps
about this and they often seem perplexed and surprised by just how simple it
can be if you don't make it complex.

~~~
korm
Can you imagine a modern SoundCloud, Spotify, Facebook, Airbnb or Slack
without a build pipeline? There's nothing appealing about that.

It's nice and simple for small projects but let's not act like putting a
script tag in HTML is some divine wisdom that newbies don't understand.

~~~
SahAssar
Yes I can. All of those except for facebook (because facebook is a sprawling
platform) seem reasonable to be able to build without a build system. And with
using es6 modules it's not "putting a script tag in HTML", you have a proper
module loading system without needing any builds or libs.

I'm not saying it's some sort of lost knowledge like Damascus steel, I'm just
saying that many people these days are so caught up into the current dogma of
JS that they never consider a simpler but still modern path.

------
halfmatthalfcat
Now if we could just get pattern matching[1] and optional chaining[2], that
would really elevate things.

[1] [https://github.com/tc39/proposal-pattern-
matching](https://github.com/tc39/proposal-pattern-matching)

[2] [https://github.com/tc39/proposal-optional-
chaining](https://github.com/tc39/proposal-optional-chaining)

~~~
cypressious
Optional chaining recently reached stage 3, the babel plugin is available and
TS is going to adopt it for 3.7.0.

~~~
chriswwweb
Oh I didnt know about TS being so close to have it, that's great news

EDIT: here is the confirmation TS 3.7.0 got tagged:
[https://github.com/microsoft/TypeScript/issues/16#issuecomme...](https://github.com/microsoft/TypeScript/issues/16#issuecomment-515160784)

EDIT 2: wow just noticed, that the issue ID is "16" and it has been open since
Jul 15, 2014 (I guess: good things take time ... ;) )

------
ojosilva
A year back I dropped a proposal idea at the EcmaScript discussion list, I
hope it get's picked up sometime.

My idea is that `let`, `var` and `const` return the value(s) being assigned.
Basically I miss being able to declare variables in the assertion part of `if`
blocks that are scoped only during the `if()` block existence (including
`else` blocks).

Something along these lines:

    
    
        if( let row = await db.findOne() ) {
             // row available here
        }
    
        // row does not exist here
    

The current alternative is to declare the variable outside the `if()` block,
but I believe that is inelegant and harder to read, and also requires you to
start renaming variables (ie. row1, row2...) due them going over their
intended scope.

As previous art, Golang's:

    
    
          if x:=foo(); x>50 {
            // x is here
          }
          else {
            // x is here too
          }
          
          // x is not scoped here
    

And Perl's

    
    
          if( ( my $x = foo() ) > 50 ) {
               print $x
          }

~~~
shortercode
I know this doesn't match up entirely with what you want syntax wise, but the
following works and I consider it quite elegant.

    
    
      {
        let row;
        if (row = await db.findOne()) {
          //
        }
        else {
          //
        }
      }

~~~
ojosilva
It's not the same. For instance, you could not use a `const` variable.

Also having "phantom" scope blocks get very nasty to read once you have more
involved logic, as the block itself has no implied meaning and the programmer
has to walk a few lines into it to get what's going on.

~~~
jazzyjackson
You could always make it a labelled block statement !

------
afandian
It's great to see JS getting some of the features of better planned languages.

But I'm still very nervous about some of the stuff mentioned here with regard
to mutation. Taking Rust and Clojure as references, you always know for sure
whether or not a call to e.g. `flat` will result in a mutation.

In JS, because of past experience, I'd never be completely confident that I
wasn't mutating something by mistake. I don't know if you could retrofit
features like const or mut. But, speaking personally, it might create enough
safety-net to consider JS again.

(Maybe I'm missing an obvious feature?)

~~~
heavenlyblue
I still don't understand why neither JSs var or let allow you to redefine the
variable with the same name.

I makes chaining things while debugging so much harder:

    
    
      let a = a.project();
      let a = debug(a);
      let a = a.eject();
    

vs

    
    
      let a1 = a.project();
      let a1d = debug(a1);
      let a2 = a1d.eject();

~~~
addicted
Without getting into the merits of allowing redefines in a loosely typed
language, the simple reason why JS can't support this is hoisting.

Any var/let statement of the form var a = 1; is interpreted as 2 statements.
(1) The declaration of the variable which is hoisted to the beginning of the
variable scope, and the (2) setting of the value, which is done at the
location the var statement is at.

Having multiple let statements would mean the same variable is declared and
hoisted to the same location multiple times. So it's basically unnecessary and
breaks hoisting semantics.

In addition, the downside risk of accidentally redefining a variable is
probably far greater than the semantic benefits of making the redefinition
clear to a reader (esp since I think that benefit is extremely limited in a
loosely typed language like JS anyways).

~~~
hajile
That's not quite accurate. It's actually the reverse.

Think of the closure as an object. It contains variables like `this`,
`arguments`, a pointer to the parent closure, all your variables, etc.

The interpreter needs to create this closure object BEFORE it runs the
function. Before the function can run, it has to be parsed. It looks for any
parameters, `var` statements, and function statements. These are all added to
the list of properties in the object with a value of `undefined`. If you have
`var foo` twice, it only creates one property with that name.

Now when it runs, it just ignores any `var` statements and instead, it looks
up the value in the object. If it's not there, then it looks in the parent
closure and throws an error if it reaches the top closure and doesn't find a
property with that name. Since all the variables were assigned `undefined`
beforehand, a lookup always returns the correct value.

`let` wrecks this simple strategy. When you're creating the closure, you have
to specify if a variable belongs in the `var` group or in the `let` group. If
it is in the `let` group, it isn't given a default value of `undefined`.
Because of the TDZ (temporal dead zone), it is instead give a pseudo "really
undefined" placeholder value.

When your function runs and comes across a variable in the let group, it must
do a couple checks.

Case 1: we have a `let` statement. Re-assign all the given variables to their
assigned value or to `undefined` if no value is given.

Case 2: we have an assignment statement. Check if we are "really undefined" or
if we have an actual value. If "really undefined", then we must throw an error
that we used before assignment. Otherwise, assign the variable the given
value;

Case 3: We are accessing a variable. Check if we are "really undefined" and
throw if we are. Otherwise, return the value.

To my knowledge, there's no technical reason for implementing the rule of only
one declaration aside from forcing some idea of purity. The biggest general
downside of `let` IMO is that you must do extra checks and branches every time
you access a variable else have the JIT generate another code path (both of
which are less efficient).

~~~
addicted
We are on the same page.

My point is having 2 let or var statements doesn't actually do anything on the
interpreter side.

If JS allowed 2 var/lets without complaining, it would be entirely a social
convention as to what that meant, since it would have no effect on the actual
code that was run.

And the social convention benefit (which could more easily be achieved with
just putting a comment at the end) is probably far outweighed by the many,
real examples I've seen where someone has accidentally created a new variable
without realizing that variable already exists in scope.

Disallowing multiple vars helps linters identify these situations (which are
far more common with var's function level scoping than let's block level
scoping).

------
rglover
That array.flat() and array.flatMap() stuff is great to see. Always having to
rely on lodash and friends to do that type of work. Exciting to see how JS is
evolving.

~~~
stevenmays
I dig it too, but you could previously flatten an array with concat, and the
spread operator. [].concat(...array)

Lodash wasn't necessary.

~~~
mantap
No this is not an alternative, it will fail if the array is too large, as you
will exceed the maximum number of arguments a function will accept (which is
implementation defined).

In general the spread operator should only be used for forwarding arguments
not for array operations.

~~~
runarberg
> In general the spread operator should only be used for forwarding arguments
> not for array operations.

Not quite. You should also use the spread operator when you are spreading an
iterable into an array:

    
    
        const unique = [...new Set(arr)];

~~~
masklinn
Seems like a needlessly unreadable alternative to Array.from unless you're
combining multiple iterables or values an iterables e.g.

    
    
        const unique = [...a, ...b];
    

You might expect that concat would work, but it doesn't handle arbitrary
iterables:

    
    
        > [].concat([1][Symbol.iterator](), [2][Symbol.iterator]())
        [Array Iterator, Array Iterator]
        > [...[1][Symbol.iterator](), ...[2][Symbol.iterator]()]
        [1, 2]

------
chriswwweb
A read a similar article few days ago (might be interesting too) (not mine):
[https://medium.com/@selvaganesh93/javascript-whats-new-in-
ec...](https://medium.com/@selvaganesh93/javascript-whats-new-in-
ecmascript-2019-es2019-es10-35210c6e7f4b)

And also here is a good recap of ES 6/7/8/9 (just in case you missed
something) (also not mine): [https://medium.com/@madasamy/javascript-brief-
history-and-ec...](https://medium.com/@madasamy/javascript-brief-history-and-
ecmascript-es6-es7-es8-features-673973394df4)

------
jeffwass
Object.fromEntries will be super useful, surprised it’s taken this long to
become a native feature.

~~~
vmasto
What would some common/helpful use cases be?

~~~
tazard
I often use Object.entries so that I can use Array.filter/map/foreach on an
object, but then I need to use Array.reduce to hack it back into an object.
Object.fromEntries solves this.

~~~
baron816
Yeah this is what I’m most excited for. filter and map and my preferred array
traversal methods. reduce can be awkward though, and people abuse it by using
it to replace or combine filter and map. fromEntries almost makes reduce
unnecessary when working with objects.

------
NKCSS
Why did they create a flatMap method? What is wrong with .map(...).flat()? Can
they improve the performance by combining it that much?

~~~
steenreem
They could have also implemented flat in terms of flatMap: flatMap(x => x)

I personally feel flatMap is a much more used method than flat, so if you want
to remove one, I would remove flat.

~~~
masklinn
> They could have also implemented flat in terms of flatMap: flatMap(x => x)

Flat can flatten any level of nesting (it just defaults to 1), so would be
difficult to implement in terms of flatMap.

~~~
contravariant
You _could_ reproduce that behaviour of 'flat' by doing something like:

    
    
        function flatten(x, n=1) {
            return n > 0 ? x.flatmap(y => flatten(y, n-1))
                         : x;
        }

~~~
masklinn
You could also blow up your stack as there is no requirement whatsoever that
javascript implementations be tail-recursive.

------
dawhizkid
arr.flat(Infinity) seems like a strange decision for flattening the entire
array - wouldn't the most common number of levels to flatten an array be all
levels, in which case I'd expect arr.flat() to flatten the whole array but in
this case it's just 1 level.

~~~
kevlened
Infinity was originally the default (which feels intuitive), but here's the
argument for changing it [0]:

"I think Array#flatten should be shallow by default because it makes sense to
do less work by default, it aligns with existing APIs like the DOM
Node#cloneNode which is shallow by default, and it would align with the
existing ES3 pattern of using Array#concat for a shallow flatten. Shallow by
default would also align with flatMap too."

[0] [https://github.com/tc39/proposal-
flatMap/issues/9](https://github.com/tc39/proposal-flatMap/issues/9)

~~~
dawhizkid
I’m trying to think of a single use case where I’d want to flatten exactly one
level of an array and can’t.

------
namelosw
Finally, flatMap is here.

I really hope there could be syntactic sugar like do expression in Haskell,
for in Scala, and LinQ in C# for flatMap instead of type limited version like
async await.

Another thing is pipe operator seems to be very welcome among the proposals.
There will be no awkward .pipe(map(f), tap(g)) in RxJS since then.

------
swalsh
Some half decent stuff in here, but I heavily disagree with changing the
output of toString. That might cause problems if someone is expecting one
output, but the new version creates something new. I don't see a reason why
they couldn't have just added a new function functionCode() or something
similar. It would give people the functionality they want, without destroying
backwards compatibility.

~~~
snek
It's already been deployed in the wild for about a year.

------
ahmedfromtunis
Can someone please point me to the rationale behind the new toString()
function?

~~~
masklinn
Rather than how complete it is, the real improvements of the proposal are:

1\. it all but requires that ES-defined functions stringify to their source
code. Pre-ES2019 that's implementation-defined

2\. it standardises the placeholder for the case where toString can't or won't
create ECMAScript code (e.g. host functions), this could otherwise be an issue
as with implementation-defined placeholders subsequent updates to the standard
_might_ make the placeholder unexpectedly syntactically valid, by having the
placeholder standard future proposals can easily avoid making it valid

3\. the stringification should be cross-platform as the algorithm is
standardised

~~~
soulofmischief
Ok that makes sense... I was really confused because that's already more or
less toString()'s behavior on modern browsers, though the whitespace
discrepancy is important to define.

~~~
masklinn
That's in part because it was fed back through browsers during the
specification process.

------
moomin
That’s a surprisingly small amount of change. I’ll leave it to others to
determine if that’s a good or bad thing.

~~~
agumonkey
Clojure too didn't change a lot. In these years of 'disruption' it feels odd.
But it may just be that we forgot stability and maturity.

~~~
moomin
Existing Clojure APIs don't change, but Clojure adds a lot more than this each
year. Look at spec, reducers, transducers &c

~~~
agumonkey
Well reducers and transducers were big announcements. IIRC clojure 1.10 didn't
add any feature of that scale, and few people mentioned that it was quite a
"smaller", without criticizing. spec seems important but is still alpha
status.

------
jonstaab
Isn't the new function string representation backwards incompatible? Having
struggled with javascript's lame error tooling I could see people actually
using it in production too.

------
intea
Why are empty elements in an array allowed? oO

[1,2,,3]

~~~
vbezhenar
They need to ensure that any combination of characters will produce some
result.

------
kuon
The part about parameter less catch reveals a lot about the philosophy of the
language. For me, silencing error like this is a bad practice. You may still
produce a sane error in the catch, but the design goes toward silencing
things.

I really love languages that force you to handle errors up to the top level.

~~~
iraldir
It does, though I wouldn't go to the same conclusion. It gives more freedom to
the developer. They are lot of cases where errors are in fact, expected.
Parsing a user inputted JSON. Making a request to check internet connection
availability. Any promise that is going to be consumed by an async - await
pattern. In those cases (and many more), you may want to try something just
simply to see if it works. It's completely different say to being on the
backend, trying to write to a database which may or may not fail.

In those cases, forcing the extra parameter in the catch, even though you are
not using it, is slightly annoying. I mean it's literally 3 characters, but in
this age of linters encouraging to not specify arguments you don't use, it
just feels unnatural.

~~~
ShinTakuya
The problem is that the catch block will catch all errors, so if a different
type of error to the one you expected occurs then it gets silently swallowed.
This is the equivalent of catching the generic Exception class in python.
Horrendous terrible practice in anything but the hackiest scripts.

------
zitto
It's very exciting to see how JavaScript is evolving!

~~~
dsego
Honest question, not playing. What is exactly exciting about it? 2020 is
around the corner and the language created back in 1995 is only now getting
features that have been standard in many other languages, either as part of
the core language or the standard library for decades.

~~~
Accacin
Well, I'd think that was pretty obvious, right? It's exciting because if you
spend the day writing JavaScript I don't give a hoot that another language has
had that feature for decades because I don't get to use that other language.

Are you implying that no other languages ever add features that other
languages have? All languages except JS are feature complete? Come on..

~~~
dsego
Well now, having an option to use other languages to script web pages, that
would be exciting.

~~~
hombre_fatal
The better JS gets, the more I want to use it. JS with Promises + async/await
is now one of my favorite languages.

~~~
dsego
How do you cancel promises?

------
pier25
I feel after ES6 and async/await in ES7 we're getting pretty meager upgrades.

IMO the three features that would make a much more significant impact in front
end work are:

\- optional static types

\- reactivity

\- some way to solve data binding with the DOM at the native level

~~~
codenirvana
Yep, waiting for private methods (Stage 3) and private-fields.

------
bitwize
Ummm... 25^2 is 625. 15^2 is 225 (see the Object.fromEntries example). I mean,
I knew JavaScript math was a bit sloppy due to the use of floating point
everywhere, but I hope it's not THAT bad...

~~~
ihuman
I think it's been changed after you posted this comment. It now shows that
15^2 is 225.

------
alexlur
That Function.prototype.toString change is probably going to break some
Angular.js code who relies on scanning function argument names for dependency
injection.

~~~
robocat
It also requires that comments get stored and waste memory: previously just
enough of the AST needed to be stored to be able to regenerate the JavaScript.
You can't throw away the comments because you can't predict where code might
do someVarHoldingAFunc.toString()

It seems like an unnecessary change - if the source needs to be accessed then
get the source file.

~~~
nkozyra
Agreed. I guess the argument for it is it's more accurately reflected but...
who cares? Maybe I'm not understanding what people do with
[function].toString() in the real world.

~~~
robocat
What's even worse is that now frameworks (and viruses) will store data in
comments.

I know this because I have wanted to use fn.toString() as part of some meta-
programming many years ago (but couldn't because comments were not stored).

I am sure a lot of effort went in to making a good decision, aiming for a good
outcome, but this smells like a bad one.

------
mhd
So, what coffeescript features are we still missing after this round?

------
bryanrasmussen
I think the flatMap method is pretty solid evidence for my contention the
language is really getting bloated now.

------
DonHopkins
Is that all there is that's new? Maybe it's a good thing that JavaScript is
finally slowing down.

------
estomagordo
Under symbol.description:

const test = Symbol("Desc");

testSymbol.description; // "Desc"

\---------

Should testSymbol be replaced with test?

~~~
mradman
Fixed the typo, thanks for the heads up!

------
Already__Taken
Why does flatmap exist? What's against `sentence.map().flat()`

------
john-foley
Hard to believe that const arr4 = [1, 2, , 4, 5]; is valid.

~~~
dspillett
Seems perfectly logical to me: an array of length 5 with four populated
elements and one empty one (3). Though I did double-check my understanding
that the length property would report 5 rather than 4 (it does).

What looks out of place to you in that example?

Would it make more sense to you with a very slightly less arbitrary example,
perhaps arr = ['Value for 0', 'Value for 1', , 'Value for 3', 'Value for 4'];
instead of simple mapping ints to ints?

Because array contents are mutable [even if the array variable itself is
declared const] that third index may be populated at a later point in the
code.

~~~
masklinn
> What looks out of place to you in that example?

Most languages don't have sparse arrays so it's really weird.

> Would it make more sense to you with a very slightly less arbitrary example,
> perhaps arr = ['Value for 0', 'Value for 1', , 'Value for 3', 'Value for
> 4']; instead of simple mapping ints to ints?

You'd usually put an explicit `null` there, especially as HOFs skip "empty"
array cells so

    
    
        ['Value for 0', 'Value for 1', , 'Value for 3', 'Value for 4'].map(_=>1)
    

returns

    
    
        [1, 1, , 1, 1]
    

which is rarely expected or desirable.

------
derefr
.

~~~
ajanuary
Doesn't the fact that flatten by default only flattens one level mean you can
still create nested output by emitted nested outputs?

~~~
2T1Qka0rEiPr
Yes, it appears so. From MDN:

> The flatMap() method first maps each element using a mapping function, then
> flattens the result into a new array. It is identical to a map() followed by
> a flat() of depth 1, but flatMap() is often quite useful, as merging both
> into one method is slightly more efficient.

[https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/Array/flatMap)

~~~
swish_bob
FlatMap is often most useful when recursing, in my experience.

------
ZenPsycho
seems like a real missed opportunity to add string.leftPad()

~~~
AsusFan
Seems like a real missed opportunity to look at the Python stdlib and add a
bunch of missing functionality to JS so that people don't have to import a
metric ton of third party libraries (or worse, write it themselves) to add
something you get for free in most other scripting languages.

Even the trim operations they added fall short of the target. In Python (and
tcl, by the way) you can specify which characters to trim.

So close, yet, so far.

~~~
snek
You can use regex for other characters, the point of the trim functions is
that they match the somewhat complex unicode whitespace.

------
la12
At this point, I'm convinced that Javascript is basically a jobs creation
program.

We go on adding fancy new syntax for little or no gain. The whole arrow
function notation, for example, buys nothing new compared to the old notation
of writing "function(....){}" other than appearing to keep up with functional
fashion of the times.

Similarly, python which was resistant to the idea of 20 ways to do the same
thing, also seems to be going in the direction of crazy things like the
"walrus" operator which seems to be increasing the cognitive load by being a
little more terse while not solving any fundamental issues.

Nothing wrong with functional paradigm, but extra syntax should only be added
when it brings something substantial valuable to the table.

Also, features should be removed just as aggressively as they are added,
otherwise you end up with C++ where you need less of a programmer to be able
to tell what a given expression will do and more of a compiler grammar lawyer
who can unmangle the legalese.

~~~
shiv86
>The whole arrow function notation, for example, buys nothing new compared to
the old notation of writing "function(....){}" other than appearing to keep up
with functional fashion of the times.

Incorrect - The main advantage is fat arrow syntax can keep lexical scope of
this current context. Hence you dontneed to implement that=this antipattern

~~~
la12
In that case, they should have deprecated the "function(){}" notation or at
least made it such that arrow function doesn't overlap it.

The current scene is that most people don't know what the real difference
between arrow and function notations and this leads to a lot more number of
bugs than if they weren't this overlapping. Overall, my point is, this just
leads to poor ergonomics and you'll have a larger number of avoidable bugs.

~~~
hombre_fatal
> The current scene is that most people don't know what the real difference
> between arrow and function notations

That's hard to believe unless you're working on the most amateur of teams.

There's a point where you have to expect people to understand the most basic
concepts of the language/tools they're hired to use. This shouldn't require
more than a simple 5min pull-aside of the junior developer.

Also, you can't change function(){}'s dynamic scope without breaking the web,
which is a major downside for your suggested upside of developers not having
to learn the distinction. function(){} was _always_ confusing from day one.
()=>{} is a move back toward intuitiveness.

------
Dirlewanger
Can we get some additions that replace the garbage one-liner `is-even`/`is-
odd` npm libraries that are a scourge?

~~~
tomduncalf
You mean something like x % 2 === 1?

I'm a JS fan but had to admit I chuckled at the implementation of is-even:
[https://github.com/jonschlinkert/is-
even/blob/master/index.j...](https://github.com/jonschlinkert/is-
even/blob/master/index.js)

~~~
soulofmischief
That's hilarious.

What's not hilarious is that, after removing the essentially useless error-
checking, is-even is literally just `(n % 2) === 1`. On one hand, JS
desperately needs a standard library, on the other hand, JS devs can be so
infuriatingly lazy and obtuse.

~~~
choward
> is-even is literally just `(n % 2) === 1`

Pretty sure that tells you if a number is odd. I guess maybe there is a reason
these libraries exist.

~~~
soulofmischief
That was just a typo, I meant the source for is-odd. I copy-pasted it. I use
that exact syntax all the time in code, you don't need to be patronizing.

------
jorangreef
Most of this is sugar, e.g. flat() and flatMap(), out of scope for a language
spec.

Function.toString being more accurate is helpful.

But real progress would be removing dangerous backtracking regular expressions
in favor of RE2:
[https://github.com/google/re2/wiki/Syntax](https://github.com/google/re2/wiki/Syntax)

