
Recreating Python's Slice Syntax in JavaScript Using ES6 Proxies - foob
https://intoli.com/blog/python-slicing-in-javascript/
======
xg15
I understand the idea behind the article - explain proxies and have a fun
project to do so, so it doesn't get boring as hell - but, in general, can we
please stop using runtime features of a language to implement syntactic sugar?

Actual syntactic suger can make a program more readable, easier to debug and
even more efficient. Those kind of hacks are neither.

It's not more readable because your expressions are limited to the original
grammar, so you have to resort to weird compromises. It's harder to debug
because you add layers of abstraction (like the proxies here) that have no
purpose at all except changing how your code looks. And it's less efficient
because those layers of abstraction will still stay with the program when it's
being executed even though you only needed them while you were writing the
code.

So, please, syntactic sugar is awesome and more thought should be given to
letting users add their own sugars - but can we do it via metaprogramming and
preprocessors and not by hacks like this?

(Disclaimer: This pattern annoys me to no end in Java, so it's gotten a bit of
a pet peeve. No fault to the author of this post who has written an excellent
article)

~~~
sametmax
Sometime it's nice. It's what makes numpy, pathlib and sqlalchemy great.

Just throwing the baby with the water is never a solution. As usual there is a
balance to be found.

~~~
xg15
Absolutely. I didn't want to demand that we all stop using sugared libraries
in an instant.

I'm more hoping that user-defined syntactic sugars and DSLs could be a focus
point in future language design - so you actually get compile time tools and
debugging support for them and don't have to resort to runtime or modeling
hacks to implement them.

------
westoncb
I used ES6 proxies recently to write a program which monitors changes on
javascript data structures, and generates operation objects reflecting the
changes to send to my data structure visualizer project[0].

You can see the source for the javascript client that does the monitoring with
proxies here: [https://github.com/westoncb/JS-
Watcher/blob/master/watcher.j...](https://github.com/westoncb/JS-
Watcher/blob/master/watcher.js) —it just works for arrays at the moment, but
the basic idea is there. (I've written another much more complete client that
does monitoring in Java; this JS one is more experimental.)

[0]
[https://www.youtube.com/watch?v=KwZmAgAuIkY](https://www.youtube.com/watch?v=KwZmAgAuIkY)

~~~
sametmax
Just use mobx. It's a robust, proven, documented way to monitor changes on
data sets.

~~~
westoncb
Looks interesting but I don't think it will be sufficient (I hope I'm wrong
about that though!). The operation data I need has to include 'addresses'
where nodes in data structures were modified.

~~~
sametmax
Store the address in each object on set.

------
spraak
Recently I implemented a wrapper function that given an object returns a Proxy
which has a get method that recursively calls itself with the accessed object
path and finally returns the object and logs the full path. (The code is not
complicated and only about as long as this post, but it's hard to type code on
a phone)

The reason for this is to give me logging for this part of the application to
aid in extracting it out to a separate service. That is, to see what from this
part of the application is actually being used/called.

Edit: I meant to say that I wanted to share this because previously I hadn't
yet found an interesting use for a Proxy.

Also, I thought of another use case: in the get method you could return a
percentage of the time a different object, i.e. an A/B test.

~~~
adrianhel
I wrote a package that creates a mock object that can be used for pretty much
anything. It's a joy to use when testing.

It's called @adrianhelvik/mock

[https://www.npmjs.com/package/@adrianhelvik/mock](https://www.npmjs.com/package/@adrianhelvik/mock)

------
zawerf
> My proposed solution here is to try to capture the spirit of Python’s slice
> syntax rather than the exact syntax itself. We can accomplish this using an
> alternative syntax where we use double square brackets and commas in place
> of colons: array[::-1] => array[[,,-1]], array[:n] => array[[,n]],
> array[-5::2] => array[[-5,,2]], and so on. This might not be quite as
> concise as Python’s syntax, but it only requires two extra keystrokes per
> slice and I think it’s as close as we’re gonna get.

I wonder if it makes more sense to write a babel plugin to support the python
syntax. Since everyone using es6 features are transpiling anyway.

~~~
alangpierce
A downside to introducing your own custom syntax with a Babel plugin is that
it's likely to break most tools that run on your source code, like editors,
ESLint, and Prettier. (And, of course, it might conflict with future ES
features.)

Also note that Babel's parser isn't pluginizable (normal Babel plugins just
run during the transform step), so the typical way to add syntax is to fork
the parser and run everything against the forked parser:

[https://babeljs.io/docs/en/next/babel-parser.html#will-
the-b...](https://babeljs.io/docs/en/next/babel-parser.html#will-the-babel-
parser-support-a-plugin-system)

------
rollulus
Great PoC, but please, don't do this. At my first gig I had to work on a huge
Lua code base, where the original author did tricks like this, effectively
creating a new language. Learning by example how things should not be done is
effective, but the suffering wasn't worth it.

------
aoeusnth1
Fun, I learned something today! I imagine the performance of proxies is pretty
terrible, so this is just a toy - but it could come in handy for implementing
equivalents of some of the nice syntax sugar that numpy and tensorflow have in
JavaScript.

~~~
olliej
Proxies aren’t actually that slow, the problem is the committee refusal to
acknowledge indexed properties as being something that actually exists.

The result is that all property names passed to the interceptor get converting
to a string.

That means if for example you were intending to proxy an array, your
performance is destroyed.

First a numeric index gets converted to a string, then when to pass that on to
your target array it has to be parsed back into a number.

In a trivial case (literally just forwarding the access) it might be possible
to fake the stringing of the property name, but if you actually end up doing
real work it becomes increasingly hard to avoid the property name escaping and
so having to be reified.

------
fpoling
I wonder why not to use the call syntax for JS slices? I.e. use a(1, 3) to
mean a[1:3]? The drawback is that 0 at the beginning of the slice cannot be
skipped, but things like a() in place of a[:] or a(i, j, k) in place of
a[i:j:k] will work.

~~~
foob
That would be a neat interface too, but it would necessitate a somewhat
different approach than the one taken in the article. You can only use a proxy
to trap operations that are already supported by the object that's being
wrapped. The _SliceArray_ class inherits from the builtin _Array_ , and it
therefore doesn't support the call operation. You would need to initialize a
second custom class that inherits from _Function_ , wrap that with a proxy,
and then use the _SliceArray_ as the actual target inside of the traps. It's
definitely doable, but it's more complicated than one might expect.

I mentioned in the article that Remote Browser is an interesting project to
look at to see some more advanced proxy usage. One of the things that it does
is to define a _CallableProxy_ class [1] which facilitates attaching a call
handler to objects that otherwise wouldn't support them. The code itself isn't
complicated at all, but it takes a bit of thought to make the pieces fit
together just right.

\- [1] - [https://github.com/intoli/remote-
browser/blob/9ffdd1b5b0a9b4...](https://github.com/intoli/remote-
browser/blob/9ffdd1b5b0a9b43426d793e529cf7d9596f986ec/src/browser.js#L11)

------
abdulkareemsn
We have a legacy angular2+ project (I know angular2+ is not that old) where we
use a global object to store state. We are using ngrx in new code , but still
need to access that monstrous global object. As other part of code is still
updating that.

I'm thinking to replace that global object with recursive proxies so that all
updates can be forwarded to ngrx store

And slowly replace all reads with ngrx selectors

------
hasahmed
"array[`${start}:${stop}:${step}`]. That’s not syntactic sugar… it’s syntactic
aspartame at best." I lol'd

------
juansgaitan
Fan of katas?
[https://www.codewars.com/kata/5a6c75c8fd5777274e000042](https://www.codewars.com/kata/5a6c75c8fd5777274e000042)

------
sun41
Just by looking at the implementation I dare to bet that this is the most
expensive slice ever invented in JS. And all that conversion and processing to
only allow for arr[-1], oh my..

------
jwatte
But why?

~~~
shiado
For articles like this I often wonder this too. I don't know if it is
developer Stockholm syndrome, a clever joke, or self-flagellation. It seems
most articles on JS are aptly summarized as "here I am overcoming yet another
critical defect of this poorly designed mess", which I suppose is laudable if
you are forced to use JS all day.

~~~
sametmax
I always joke with my students that the most popular projects in js are
designed to not write js.

Tongue in cheek but still, webpack, babel, react, jquery, coffeescript,
lowdash and typescript are all attempts to make the pain of using the js
ecosystem go away by patching in things that has existed for decades elsewhere
at the price of huge infrastructure and organisational cost.

That such a terrible tech remains with a monopoly on the most popular and
awesome platform in the world still astonishes me to this day. And humbles me
in a way.

But what infuriates me is that a lot of js devs will actually claim that it's
great. The stockholm syndrom is strong indeed.

------
amelius
Can anyone summarize what the resulting syntax looks like?

~~~
foob
There's a table in the GitHub repository that shows the inputs and outputs for
various equivalent slices in JavaScript and Python [1]. If you're familiar
with the Python slicing syntax, that should make the syntax mapping fairly
clear. The way to translate a Python slice definition to JavaScript is to use
double square brackets instead of single ones and to replace the colons with
commas. So _array[::-1]_ becomes _array[[,,-1]]_ , _array[3:]_ becomes
_array[[3,]]_ , etc.

[1] - [https://github.com/intoli/slice#for-people-who-know-
python-a...](https://github.com/intoli/slice#for-people-who-know-python-
already)

------
cyberpunk0
Why do so many JavaScript devs use 2 spaces for tabs? Its terrible for quickly
reading through code

~~~
davidmurdoch
Someone (@feross) created something called StandardJS, and even bought the
domain standardjs.com, and started advertising it as a great style guide. I
initially thought it was a joke or parody, as my own style is pretty much
opposite of the guide.

Anyway, it says to use two spaces. It got super popular and here we are now.

~~~
workinfunk
JavaScript has commonly used 2 spaces for years before "StandardJS" (which is
now out-of-date because certain es7 features necessitate line-terminating
semicolons, but I digress).

CoffeeScript was a big catalyst, and I think Rails was as well, but it
probably would have happened anyway. Of the languages I use regularly, the
only ones for which 2 spaces is not accepted as normal are Python (which uses
4, probably because it was set in stone decades ago, before people even knew
how to write code) and Go (whose auto-format uses tab characters, probably
because Rob Pike is a stubborn prick).

Really, 4 spaces is an awful lot, unless your eyesight is failing.

