

Metaprogramming in ES6: Symbols and why they're awesome - jessaustin
http://blog.keithcirkel.co.uk/metaprogramming-in-es6-symbols/

======
current_call
_If programming can be described as "making programs", metaprogramming could
be described as "making programs making programs" \- or something. You
probably use metaprogramming every day perhaps without even noticing it._

 _Compared to other languages like Ruby or Python, JavaScript 's
metaprogramming features are not yet as advanced - especially when it comes to
nifty tools like Operator Overloading, but ES6 is starting to level the
playing field._

What isn't metaprogramming now?

This feels very relevant.
[http://journal.stuffwithstuff.com/2013/07/18/javascript-
isnt...](http://journal.stuffwithstuff.com/2013/07/18/javascript-isnt-scheme/)

~~~
thomasfoster96
I think that link needs a bit of updating - JS has lexical scoping and tail
call elimination now.

~~~
current_call
I tried to create an infinite loop in Firefox and got a "InternalError: too
much recursion". Then when I tried it in Chrome I got "Uncaught RangeError:
Maximum call stack size exceeded".

function x() { return x(); }

~~~
thomasfoster96
That's still expected?

~~~
current_call
It's a recursive tail call. It should run forever, like the following Scheme
code.

(define (foo) (foo))

(foo)

~~~
thomasfoster96
Ah, right, yes I get what you mean.

The main problem is still that Chrome and Firefox don't support tail call
elimination. Babel (6to5) and Traceur do.

~~~
strmpnk
They don't. They support an optimization for self recursive calls but not
general tail call elimination.

------
lispm
Symbols in ES6 are much like symbols in Lisp. Basically much where Lisp
software uses symbols applies to ES6.

The main difference: Lisp has simple printed representations for interned,
uninterned and keyword symbols.

    
    
        CL-USER 23 > (let ((color-interned-symbols   '  (yellow   green   red))
                           (color-uninterned-symbols '(#:yellow #:green #:red))
                           (color-keyword-symbols    '( :yellow  :green  :red)))
                       (list color-interned-symbols
                             color-uninterned-symbols
                             color-keyword-symbols))
        ((YELLOW GREEN RED) (#:YELLOW #:GREEN #:RED) (:YELLOW :GREEN :RED))
    

Lisp also has packages for namespaces of symbols.

Here you can learn more about computing with symbols, in Lisp:

[https://www.cs.cmu.edu/~dst/LispBook/](https://www.cs.cmu.edu/~dst/LispBook/)

~~~
roopeshv
url doesn't work (Forbidden, You do not have permission to access the
requested address (URL)).

In any case, I think the book parent is referring to:
[http://www.amazon.com/Common-LISP-Introduction-
Computation-E...](http://www.amazon.com/Common-LISP-Introduction-Computation-
Engineering/dp/0486498204)

~~~
lispm
Maybe on your side. Not here. Works fine. The book is for download from there.

------
kowdermeister
Please, don't ever use foo/bar examples. No better way to bore and confuse the
reader. It's the worst way to educate people about the possibilities of the
thing you want to teach about.

~~~
carussell
In the past, I hardly ever used foo and bar in my examples, for that reason.
Over the last two years or so, I've strived to _start_ using them.

There's history of using foo/bar/baz in examples. So it's a very quick way to
signal to the reader, "What this is isn't the important part. The meat of the
discussion isn't in this; he crux of it lies elsewhere," and that's a pretty
important thing.

There's a cost in using "real" examples. There's a cost to you the author in
coming up with them, and then there's the cost to the reader who has to unpack
the domain-specific concepts in the example you're synthesizing and
disentangle it from what you're actually trying to demonstrate.

Do use foo and bar whenever possible.

------
zmmmmm
The global symbol registry sounds intriguing but dangerous. Does it now
provide a mechanism for client side cross domain communication? The browser
spends most of its time isolating different domains, but in this instance the
feature seems to be explicitly added to allow for some kind of cross domain
interaction. In turn however that would seem to bring with it all the security
problems of allowing it - phishing sites can frame your web site and then farm
all the information out of the global symbols. Which means any use of them
would have to be very careful.

Does anybody have any insight on whether my interpretation here is correct?

~~~
girvo
Iirc, there are still ways to communicate between frames (postMessage rings a
bell?) -- the global symbols don't really hold any information though, and are
mostly just markers that are used in the code itself :)

~~~
AdieuToLogic
> the global symbols don't really hold any information though, and are mostly
> just markers that are used in the code itself

Global flags always represent information, be it to the people who code them
up or attackers who leverage their existence.

~~~
to3m
I can't see any reliable extra information available, based on the description
in the article.

The only thing I can think of offhand: time the execution of Symbol.for.
Mostly, this won't be terribly illuminating, because the bulk of the work
(hashing input + looking in table) will be the same in both cases, and won't
take long. But, assuming the registry is a hash table, what you could do in
one script is pollute the registry with enough strings to trigger several hash
table rebuilds (which you can detect by outsize results from Symbol.for - you
add enough strings to provide a good representative set of timings).

Then another script could detect this by doing the same operation and seeing
if no call to Symbol.for took much longer than any other. This gets you 1 bit
of information... is that useful?

(Also, I wonder what they do about symbol table exhaustion.)

As for the results, they don't themselves hold any extra information, I don't
think. Regarding Symbol.for: if the table contained the requested symbol
already, it will return that symbol. If it didn't, a new symbol will be added,
and returned. These are the only two options, and the caller has no way of
knowing which was taken.

And regarding Symbol.keyFor: if the input was previously returned by
Symbol.for, returns true. And if not, returns false. Neither tells you
anything because the caller can only have acquired the symbol by doing one of
these two operations itself, meaning it isn't getting any information that it
didn't in theory have already.

------
braythwayt

      > the only way to get the Symbols within an Object
      > is Object.getOwnPropertySymbols
    

Also, Reflect.getOwnKeys.

------
seanalltogether
This is a bit confusing to me, if the key doesn't matter and no two symbols
are equal, why would you ever use

var foo = Symbol('foo')

over

var foo = Symbol()

~~~
kannanvijayan
Mostly as a debugging aid. Stringifying the symbol will let you get at the
internal string, which can be useful for tooling, introspection, etc.

------
ww520
If symbols of an object cannot be iterated over, does that mean they cannot be
serialized into JSON? Also a symbol doesn't have a unique string
representation, Symbol('foo') != another Symbol('foo'), does that mean they
cannot be deserialized from JSON?

~~~
loganfsmyth
JSON is a very specific subset of JavaScript, and there is no literal syntax
for Symbols, so there is no defined way to represent a symbol at the moment.
Unless you wanted to encode them as a function call or something, which sounds
terrible.

------
thomasfoster96
I hadn't really been able see how useful well-known Symbols would be until I
read this. My only thoughts now are that overriding binary operators in ES7
would be an amazing feature addition.

------
leishulang
mirror:
[http://webcache.googleusercontent.com/search?q=cache:9lmo3WI...](http://webcache.googleusercontent.com/search?q=cache:9lmo3WIRFD4J:blog.keithcirkel.co.uk/metaprogramming-
in-es6-symbols/&hl=en&gl=ca&strip=1&vwsrc=0)

Edit: now it's back up online.

Any examples of using symbols for variants or flyweights ?

------
jarek-foksa
Why window.Symbol looks like a constructor, but works like a factory? Wouldn't
it make more sense to have either a regular constructor (let symbol = new
Symbol()) or a regular factory (let symbol = createSymbol())? If it looks like
a duck, it should also walk like one.

When subclassing, what is the advantage of [Symbol.toStringTag] getter over
the toString() method override? Is it just another way to do the same thing?

~~~
gchpaco
The Symbol workaround cannot ever clash with uses of `with`. Granted almost
everybody considers `with` to be a bad idea nowadays, but that's part of the
rationale.

------
zipolupu
The second example of Symbol.isConcatSpreadable should assert "z" equality,
not "y"

------
nawitus
I think "getOwnSymbols" should be "getOwnPropertySymbols".

~~~
carussell
This can be the copy editing subthread.

See zipolupu's comment
[https://news.ycombinator.com/item?id=9790181](https://news.ycombinator.com/item?id=9790181)

The article also says:

> When you see code like rho == lho it could be converted into
> rho[Symbol.isAbstractEqual](lho), allowing classes to override what == means
> to them.

While there's not any technical error here (the examples follow proper alpha-
conversion), the "l" and "r" in lho and rho stand for "left" and "right",
respectively, so they should be switched.

In the Symbol.match example, "return [find];" should be... "return [index];"?

