
Show HN: Eslisp – Minimal JavaScript S-expression syntax and macro system - an_ko
https://github.com/anko/eslisp
======
jrapdx3
To my taste, Lisp's hierarchical list notation is by far the most natural,
intuitive way to represent HTML. I exploited this Lisp feature to create a web
server using Scheme and C (a 5-year project).

While my server still works well, it's obvious Node and JS have become
predominant tools in the space. I used these tools for my most recent project
and while not too difficult to get up to speed, it lacked the elegance and
simplicity I preferred.

Eslisp looks promising, surprisingly close to Lisp/Scheme I'm familiar with.
I'm thinking that a few changes might make it even closer. For example, one
could use "define" instead of "=" to declare bindings, or "begin" instead of
"block".

The "this" syntax in macros is reminiscent of the arguments to ER syntax
transformers which can be used to operate on elements in macros. Is it
possible to simplify it further? Something like (this/evaluate) instead of (.
this evaluate)?

Just ideas that pop into mind...

I agree that using s-expr syntax to create macros makes is much better than
the elaborate syntax of other JS macro systems. I'm looking forward to
learning more and trying it out.

Eslisp looks like it will be excellent.

~~~
an_ko
Many thanks for your thoughts.

> Lisp's hierarchical list notation is by far the most natural, intuitive way
> to represent HTML.

HTML-representation is also important to me, and it's how I got started on
eslisp actually. I wrote an XML templater
[https://github.com/anko/whatxml](https://github.com/anko/whatxml) for
LiveScript that abused the ".." cascaded-function-call feature to create XML
hierarchies. It took me a while to realise it was basically an S-expression in
disguise!

I'm working on an "eslisp-html"-macro that compiles to an HTML-templating
function. Here's the gist of it so far
[https://gist.github.com/anko/f8ecc66a99a8ec656b49](https://gist.github.com/anko/f8ecc66a99a8ec656b49)
No implementation yet, just an early-ideas readme with an example. What do you
think?

> For example, one could use "define" instead of "=" to declare bindings, or
> "begin" instead of "block".

If you want to use different names for the built-in macros, you are welcome
to. Aliasing them is just a matter of "(macro define =)", then "define" does
the same as "=".

> The "this" syntax in macros is reminiscent of the arguments to ER syntax
> transformers which can be used to operate on elements in macros. Is it
> possible to simplify it further? Something like (this/evaluate) instead of
> (. this evaluate)?

Property access can be sugared to "this.evaluate" with the eslisp-propertify
macro [https://github.com/anko/eslisp-
propertify](https://github.com/anko/eslisp-propertify) It's quite convenient
if you use it as a transform macro
[https://github.com/anko/eslisp/blob/master/doc/how-macros-
wo...](https://github.com/anko/eslisp/blob/master/doc/how-macros-
work.markdown#transform-macros) from the command line, like this
[https://github.com/anko/eslisp-fancy-
function/blob/master/ma...](https://github.com/anko/eslisp-fancy-
function/blob/master/makefile#L6) Is that what you mean?

~~~
jrapdx3
> ...an "eslisp-html"-macro that compiles to an HTML-templating function...
> Here's the gist of it so far ... What do you think?

Interesting! Similar to Scheme templates:

    
    
        (let* ((admintype (...)) 
               ...
               (htm (htdoc
                  `(html
                     (head
                       (meta (@ title "My site")))
                     (body
                       (div (@ class "banner") 
                         (img (@ class "logo" src "logo.gif" 
                                 title ,(get-curr-url))) ...
    

Basically it just sets up variables for use in the let* scope, and calling the
htdoc procedure that parses the quasiquoted list, returning the prepared html
as a string (written to the client-connected socket).

The htdoc proc is surprisingly simple, and I've implemented it in other Lispy
languages without much trouble. I'd be real interested to try it in Eslisp
once I'm familiar enough with it.

> Property access can be sugared to "this.evaluate" with the eslisp-propertify
> macro ... Is that what you mean?

Possibly. I need to learn it more thoroughly.

Of course I realize the "(. id1 id2 ...)" form expands to object notation. My
comment just reflected that to me the leading dot expression looks "noisy", a
bit distracting.

In Scheme, identifiers can contain punctuation like ".", so "this.evaluate"
could name a procedure, macro, etc. To my eyes, "(this.evaluate)" is visually
easier to parse. In my head, the string "this.evaluate" looks like an atom
that expands to the operator this.evaluate(), which I think is what it means.

I'm sure I'll get used to the notation as it is, this is just my immediate
impression. However, mildly "sugaring" the representation might make it more
accessible, especially to people not already enlightened by and in awe of the
magic of s-expr.

Edit: getting the code section to look like code...

------
beders
Listen to an old man that has gotten his feet wet with Allegro Common Lisp
back in the day:

Avoid macros if at all possible. Use functions if you can.

Special forms that don't follow the standard evaluation rules are putting
additional mental load on the readers of your code. If you go overboard with
macro use, good luck having other people understand your code.

If you are a lone coder, feel free to go happy-crazy with macros. If you are a
team, just stay with standard lisp as long as you can.

~~~
sklogic
You do not understand macros at all. One should _always_ prefer macros to
anything else. Macros must be used to implement hierarchies of composable
eDSLs - by far the most powerful and advanced way to handle abstraction.
Functions are not a match to this.

Heavily DLSed code is much more readable and maintainable than your mess of
functions.

------
dherman
Fun :) Back in my Racket days (back then it was known as PLT Scheme and
dinosaurs roamed the web) I did a similar thing:

[http://planet.racket-lang.org/package-
source/dherman/javascr...](http://planet.racket-lang.org/package-
source/dherman/javascript.plt/9/2/planet-docs/javascript/pjs.html)

The Javadot notation was really convenient -- might be worth checking out:

[http://jscheme.sourceforge.net/jscheme/doc/javadot.html](http://jscheme.sourceforge.net/jscheme/doc/javadot.html)

~~~
an_ko
Wow, many thanks for sharing!

PJS is very neat and its documentation very clear. I'm surprised how little
JavaScript has changed in 16 years…

I've got a Javadot-like thing [https://github.com/anko/eslisp-
propertify](https://github.com/anko/eslisp-propertify) for converting a.b.c →
(. a b c). Eslisp uses "." for property access, like PJS' "field". It hasn't
got a next-line (.property args) thing though, which seems useful. I'll be
trying that.

------
joubert
How does its macrology stack up against sweetjs?
[http://sweetjs.org](http://sweetjs.org)

~~~
an_ko
Eslisp macros are compile-time functions that operate on code consisting of
lists.

Sweet.js macros are special-case separate structures that have to introduce
special syntax, pattern-matching rules and a whole lot of other cruft, mainly
to deal with JavaScript not being homoiconic.

tldr eslisp macros are prettier, otherwise quite similar.

------
Kjeldahl
Looks great. At the very least it needs a built-in function shortcut; people
are already used to ES6/Coffeescript shortcuts. Yes, I understand I can
probably macro it myself, but that doesn't mean it's not needed in core.
Anyway, looks like a great piece of work!

~~~
an_ko
If you mean shortcuts like argument-splats and implicit return-statements,
[https://github.com/anko/eslisp-fancy-
function](https://github.com/anko/eslisp-fancy-function) is on npm.

All you need is to npm install it and do

    
    
        (macro function (require "eslisp-fancy-function"))
    

If you want it in all files, it's safe to cat that onto the start of each as a
build step.

I do see your point though—it's a very common thing for people to want to use.
I'm adhering strictly to UNIX design priciples for now because I'm afraid of
featuritis, but perhaps that will change as the project matures.

I'm playing with the idea of having a separate convenience "getting started"
package called "eslisp-coffee" or something, which acts like the core eslisp
compiler but with a certain set of macros attached by default. What do you
think?

~~~
Kjeldahl
eslisp-fancy-function looks great. One standard package with a lot of sensible
defaults - based on learnings from ES6, Coffeescript etc - sounds like a great
idea. Again, great work!

------
dangoor
This reminds me of wisp:

[https://github.com/Gozala/wisp](https://github.com/Gozala/wisp)

~~~
an_ko
wisp was indeed an inspiration! A pretty fundamental difference is that eslisp
macros are ordinary JavaScript functions, which you can put on npm, and write
in any language. In wisp, macros are magic compiler-internal things.

Here's a brief comparison to a few other JS-lisps too:
[https://github.com/anko/eslisp/blob/master/doc/comparison-
to...](https://github.com/anko/eslisp/blob/master/doc/comparison-to-other-js-
lisps.markdown)

~~~
Gonzih
> (The non-JVM self-hosted implementation does not yet support macros at the
> time of writing.)

Fixed, you can remove this line :)

------
nanny
Neat. Why would I use this over ClojureScript or Parenscript?

~~~
an_ko
ClojureScript tries to be Clojure, and Parenscript tries to be Common Lisp.
Eslisp tries to be JavaScript; it's intended to just be an obvious one-to-one
syntax replacement.

I wrote a brief comparison in the docs:
[https://github.com/anko/eslisp/blob/master/doc/comparison-
to...](https://github.com/anko/eslisp/blob/master/doc/comparison-to-other-js-
lisps.markdown)

~~~
aidenn0
Really only macros are in common lisp for Parenscript. Parenscript very
strongly tries to be close to JavaScript.

~~~
an_ko
That's very true. For anyone with an existing Common Lisp stack who wants to
use CL macros to write JS, Parenscript is great.

I wanted to write JS macros in JS, hence eslisp.

