
Show HN: Parinfer – a simpler way to write Lisp - undershirt
http://shaunlebron.github.io/parinfer/index.html
======
Blackthorn
To copy a comment I made elsewhere: I'm seriously impressed. This is amazing.

Paredit always ended up being a requirement for me, but this takes it to the
next level. What other editing problems out there could have excessive hotkey
usage replaced by something automatic with the same level of power, I wonder?
This is a great example of an area where user interaction experts can really
have a lot of impact, and I think shows why engineers ought to be more
accepting of disciplines like that.

------
chrisoakman
Hi folks -- I've been working with Shaun on the tail end of this project.

Have an early prototype of Parinfer working with the Atom editor here:
[https://github.com/oakmac/atom-parinfer](https://github.com/oakmac/atom-
parinfer)

It's pretty early software, but feedback appreciated :)

~~~
cnp
Parinfer + Atom -- what a dream come true. Awesome work. (Edit: just took it
for a spin, and it "just works", even with atom-vim -- doubly awesome. I
really feel like having to navigate parens while learning the language was
such a barrier to diving in to ClojureScript. Cant wait to get started.)

~~~
chrisoakman
This comment makes me so happy; thank you :)

I'm glad it "just works"!

~~~
omginternets
Your project seriously makes me want to learn lisp.

~~~
tripzilch
Me too!

And I already wanted to learn Lisp. Because I kind of half-assumed this tool
to be there already :-P

I always assumed, obviously Lisp didn't get this popular if people had to keep
track of all their parentheses. Sure they probably had to in "the old days"
(although I wonder even when?). It just seems like a tedious thing to have to
do in order to work with such a consistently structured language.

Of course you can get most of it with highlighting matching parens and auto-
formatting shortcuts. Like most languages.

But I kind of expected Lisp tooling to go the extra mile. Because it is _that_
structured.

From my limited knowledge of Lisp, I figured some vague ideas on how such a
tool would work / should be designed. It's kind of cool to see that the
author's initial steps match up with that idea: define a grammar, some
invariants, and "have at it" (ok, so I didn't figure that far). And then he
made it work!

Maybe a silly question, but since the author is in this thread: Was it hard to
make? :-)

BTW the idea of switching between these two modes is a very clever approach to
keeping certain invariants invariant while still being able to modify the code
--because the trivial way to preserve the invariant would be the tool
immediately reverting your edit :)

------
ChicagoBoy11
I don't know what's more impressive: The work itself or the beautiful
documentation which accompanies it. Very impressed!

------
da4c30ff
For emacs, there's the electric-indentation-mode and the fantastic aggressive-
indent-mode.

[https://github.com/Malabarba/aggressive-indent-
mode](https://github.com/Malabarba/aggressive-indent-mode)

~~~
ruricolist
A more direct comparison for Emacs would be `adjust-parens`:

[https://elpa.gnu.org/packages/adjust-
parens.html](https://elpa.gnu.org/packages/adjust-parens.html)

~~~
alphapapa
Wow! This is fantastic! This seems to do just what Parinfer does. Why isn't
this mode more well-known?

This makes it even better:

    
    
        (defun my/lisp-dedent-adjust-parens ()
          (interactive)
          (save-excursion
            (x4-smarter-beginning-of-line)
            (call-interactively 'lisp-dedent-adjust-parens)))
        (defun my/lisp-indent-adjust-parens ()
          (interactive)
          (save-excursion
            (x4-smarter-beginning-of-line)
            (call-interactively 'lisp-indent-adjust-parens)))
        (local-set-key (kbd "<M-left>") 'my/lisp-dedent-adjust-parens)
        (local-set-key (kbd "<M-right>") 'my/lisp-indent-adjust-parens)
    

Now you can just M-left and M-right to adjust the indentation of any line,
regardless of where the point is, and adjust-parens fixes the parens for you.

------
munificent
Wow, this is a fantastically documented and executed idea. I'm not enough of a
Lisper to have an opinion on the content (though it certainly seems cool), but
the presentation is top notch.

~~~
aidenn0
As a lisper, the content is similarly well executed. It's a rarity to see both
in one place.

------
lispm
> Most programming languages have several syntax rules. Lisp has one:
> everything is a list.

Oh, not again.

The author is confusing s-expression syntax with Lisp syntax. Even
s-expressions are more than lists (conses, cyclic conses, symbols, vectors,
strings, various numbers, structures, ...) and the s-expression reader knows
about more than pure s-expressions - for example comments.

Not every s-expression is a valid Lisp expression. For example (let "let") is
not a valid Common Lisp expression. The syntax for LET is:

    
    
        let ({var | (var [init-form])}*) declaration* form* => result*
    

This is also using the syntax definition for 'declaration' and 'form'.

Lisp has built-in syntax (provided by special operators in Common Lisp) and
arbitrary complex syntax provided by macros.

This is valid for other languages with a notation based on s-expressions, like
Scheme or Clojure.

~~~
Blackthorn
We all knew what it meant, including the author. This is pointlessly pedantic.

~~~
nickpsecurity
I'm the first to call out grammar nazi's and pedantic crap. Despite that,
lispm is right to call it out for a correction. It's a factually inaccurate
statement that's gives non-LISP readers a misleading impression about what the
language and its machine code are likely capable of. It's also spread like
wildfire due to constant repetition as lispm noted.

So, it's worth mentioning and replacing with something more accurate.

------
cmrdporcupine
Looks like what Jetbrains would implement if they implemented a proper Lispy
language support.

Very nice.

~~~
nnq
Am I the only one who is _incredibly annoyed_ by how Jetbrains IDEs indent
code?! Really, the fact that they totally fail to indent even mildly broken
code is infuriating. At least ten times a day I feel like yelling at webstorm
"YES, I know that that code is broken, that there are two unpaired {s and one
unpaired ', but I'll fix that F LATER! NOW I just want to type this code
_here_ and have it properly indented even if there's broken code 10 lines
above it and 20 lines below it. I KNOW, and I DON'T care NOW!".

I just want to download stuff from my head to the editor before I forget it,
then later fix the mess, and have the IDE/Editor still help me in the
meantime. If it needs to temporarily revert to a caveman-style indent-like-
the-previous-line or event to temporarily _disable_ auto-indent, I'm ok with
that, but don't horribly mess up what I type in failed attempts to
semantically auto-indent broken code that I don't wanna fix _right now_...

~~~
taternuts
No - you aren't the only one, this infuriates me sometimes. The worst is when
you have a file that someone accidentally mixed indentations or something, or
use 2 spaces instead of 4 (even 4 spaces seems to break for me) for
continuation/promise stuff. I'll enter insert mode and it'll be in-explicably
tabbed out 5-6 times further than it should and I have to exit insert mode and
manually move over to the correct spot or finish typing the function in shit-
mode then manually fix the indenting. There's been several times where I've
given up in frustration and opened up VIM or sublime text because I don't want
to deal with it anymore.

~~~
andrewchambers
Parsing correct code is easy. Parsing broken code is really hard because they
have no way to predict the mistakes people will make.

------
davexunit
Good to see more people working on editors for Lisp, but I don't really see
this as an improvement over the (immaculate, IMO) Paredit for Emacs.

~~~
danneu
If it lets people write lisp with the same ease of writing Javascript or
Python (i.e. without investing time learning a tool specifically for editing
s-expressions), then that's an advantage over Paredit kinda like Sublime's
advantage over Vim/Emacs.

Trying it out in Atom, it looks really promising. You just kinda start
writing, like on the blank line at the end of a function body, and Parinfer
fills in the parens depending on your indentation level in an intuitive
fashion, merging your form into the tree above it.

In fact, it seems to strictly complement Paredit. Parinfer puts the parens
where you want them, and Paredit is there for explicitly editing the tree.

~~~
sklogic
Did I miss something and Sublime somehow got some advantage over Emacs? When
did it happen and what kind of advantage, exactly?

~~~
roryokane
Sublime Text’s advantage in this context is the advantage that all GUI text
editors share, including Atom and GEdit. This advantage is a combination of
being more familiar and having better defaults.

Some ways that Sublime is more familiar are that it uses the OS-standard
editing hotkeys, such as moving by words with Ctrl-arrows or Option-arrows. It
also uses OS-standard terminology, like “close window” instead of “kill
buffer” and “paste” instead of “yank”. The result is that new users don’t have
to learn anything to be productive – they just edit as if they are in
Microsoft Word, while enjoying the programming-specific features of a text
editor such as syntax highlighting and block folding.

The better defaults include easily creating more than one scratch buffer (just
run “New File” multiple times), automatically balancing parens when you type,
support for pixel-by-pixel smooth scrolling, and scrollbars that don’t
confusingly shrink when you start scrolling past the end of the buffer. I’m
sure Emacs supports all these things, but you have to manually configure it to
do so first. Many users are scared off from Emacs after seeing that it
requires up-front investment to get features that other editors already
provide with no work.

~~~
sklogic
> more familiar

To whom? There is still a _huge_ number of incompatible text editor cultures.
Mac bindings, Microsoft bindings, Wordstar, etc.

> better defaults.

There are dozens of "better" .emacs pre-cooked packages. Choose any. No need
to stick to the out of the box defaults.

> OS-standard

Which OS?

> he result is that new users don’t have to learn anything to be productive

The "new" users had to learn all those "OS default" bindings and terminology
first. Not that "new" in my book then.

> it requires up-front investment

They simply forgot their up-front investment into learning the defaults of
their chosen OS. I understand that - I still cannot use Microsoft Office,
can't be bothered to learn it.

So, the only advantage is familiarity to those who already invested a lot of
time into learning something else. Nothing of a value for the _new_ users.

~~~
roryokane
Yes, pre-cooked packages of defaults make things a lot easier for new users,
but not quite equivalent to GUI text editors. I don’t get the impression that
most people being introduced to Emacs are told to use a package of defaults. I
think a lot of users will want to try out Emacs, so they download Emacs, then
try to configure some things they don’t like by Googling the specific issues.
After they have too big a list of things they don’t like about the defaults,
they go back to their old editor, without ever learning of the existence of
defaults packages. Which is a shame.

Apart from defaults, I agree that GUI text editors provide no advantage to
_completely_ new users. But I’m not sure that “ _new_ users” of text editors,
unfamiliar with OS conventions, is a category worth talking about – I think
it’s really small. It shouldn’t take more than a year for a new computer user
to absorb the OS conventions while browsing the web and writing documents. And
how many people might want to try out Emacs within their first year of using a
computer?

I guess that about 10% of eventual programmers started trying out programming
in their first year of computer use – the other programmers started later in
life. The number of fledgling programmers who care about editors enough to
look into Emacs has to be even less than that – it’s probably something like
1% of all programmers. The other 99% of programmers will find GUI editors more
familiar than Emacs, because they have already internalized the conventions.

~~~
sklogic
> GUI text editors

Emacs is a GUI text editor too, btw.

I can be wrong, but my impression was the opposite - all the new tutorials I
saw recommended one or another starter pack with all the bells and whistles
out of the box.

> while browsing the web and writing documents

I'm not sure if a proportion of users who write documents in a specialised
software (like Word) is significant enough. And web browsing is largely
clicking, no hot keys are used in general, so no habits to be picked up.

Another thing to consider is that now more and more users are coming from
mobile platforms, without any desktop exposure whatsoever. For them, any
desktop conventions are equally alien.

~~~
roryokane
Good point, I hadn’t considered the new generation of mobile-first users.

> I'm not sure if a proportion of users who write documents in a specialised
> software (like Word) is significant enough.

I thought that most computer users learn to use computers while in school, for
which they use a word processor to write essays and reports for the teachers.
From around 2004 to my graduation in 2009, my middle and high school’s
teachers even _required_ essays to be typed and printed, so that they wouldn’t
have to struggle to read students’ handwriting.

I guess some students prefer writing essays on paper, but those students
probably wouldn’t become potential Emacs users.

Am I missing a common backstory for programmers that doesn’t involve using
computers to write reports for school?

~~~
sklogic
To be honest, I'm not quite aware of the current school requirements here, in
my days all reports were hand written. But I've seen quite a lot of young
people (here in the UK, do not know about the other countries) who obviously
got no defining exposure to any desktop software at all. It is almost funny to
see people discovering the copy and paste idea for the first time.

And with all the tablets and smartphones, even the basic touch typing skill is
now extremely rare among the young.

------
drcode
Wow, I totally want to do my programming with this now... is there support for
matching indents on 'let, as with align-cljlet?

------
namuol
Taken a step further, a structural editor could prevent incorrect syntax
outright, and even allow you to view/edit the structure of your code in ways
that simple syntax-highlighted text cannot.

Here's one such experimental editor:
[https://github.com/darwin/plastic](https://github.com/darwin/plastic)

Edit: I see now that Parinfer refers to Plastic under its Acknowledgements
section. :)

------
mfikes
I'm wondering what Parinfer would feel like if used when editing the
expression to submit in a REPL. (In other words, it could be nice outside of
source editors.)

~~~
danneu
This is a good point. I immediately dismissed Clojure when I played with the
in-browser REPL [http://www.tryclj.com/](http://www.tryclj.com/). Within
moments, I imagined the daily plight of the professional Clojure developer
tediously balancing parens like I had to do in that REPL.

Had I known how polished my workflow would be a year later with Paredit +
Emacs in-buffer line-by-line code evaluation, or that tool-supported
s-expression editing would let me write/refactor/move/nest code much faster
than in other dynamically-typed languages, I would've stopped what I was doing
and started learning Clojure immediately.

But you don't know any of that if you're someone going to
[http://www.tryclj.com](http://www.tryclj.com) for the first time.

Seems like Parinfer could be used in environments where you don't have Paredit
available or, perhaps more importantly, where the user hasn't already
credentialized in a niche tool for editing s-expressions.

------
ZenoArrow
In the Parinfer introduction, 'C Style indentation' is mentioned.

What I can't understand is why 'C Style indentation' isn't the default
convention, at least in modern user-friendly Lisps. One of the biggest
problems people have with Lisp is the readability of grouped parentheses, but
the C Style indentation example shows you can write Lisp without them.

I get that it's nice when programs fit in less lines of code, but isn't
readability more important than line count?

~~~
ScottBurson
C brace style has nothing to do with readability. Indentation alone shows the
block structure just fine; Python proves this.

The purpose of C brace style is to make _editing_ easier in primitive editors
that don't do delimiter matching. In such an editor, selecting where, in a
long string of closing delimiters, to insert a new element requires counting
the delimiters, which is tedious and error-prone. With delimiter matching, the
editor does the work for you, so splitting delimiters across lines is
completely unnecessary.

~~~
phillmv
Eh, yes and no. Python at least has `elif` statements.

When execution control depends on the order of nested arguments, I find "c"
style to really enhance the following contrived example, even with rainbow
parens:

    
    
      (if true 
        (whatever this is an (example
           that is (kind of hard)
             (-> to 
               (read %))))
         (this is an ELSE but hard to figure out))

~~~
danneu
Your example arbitrarily indents and new-lines. If that's what's on the table,
then nothing is really going to help you.

    
    
        if (x 
        > 100) { console
          .log('hello') } else 
        { console.log
        ('world')}
    

When it comes to if/else and if you indent with some sanity, then lisp is like
Python: if the 'else' branch is too hard to spot or if you forget the context
by the time you reach the 'else', then your 'if' branch is simply too many
lines long. Or you should flip the condition so that the short branch comes
first.

Meanwhile, I can't even write your lisp example as-is in any of the editors I
tried it in.

------
sdegutis
When I started working on a Clojure web app full time, I forced myself to
spend a week learning and configuring Emacs, and learning Paredit.

To this day I have not found a tool that works as well as Paredit for editing
and navigating s-expressions.

Parinfer looks like a cool subset of Paredit's abilities. I'd love to see it
expanded to include all of what Paredit can do.

EDIT: I was mistaken, Parinfer does things Paredit doesn't. They could totally
work together, that'd be pretty awesome.

~~~
rntz
I see no reason they couldn't coexist. Parinfer to guess paren insertion;
paredit to cover the operations parinfer can't infer.

I'm more concerned that Parinfer appears to be modal, based on whether you
manage indentation or parens. This seems overly complicated; it would be nice
if there's some elegant way to merge the features of both into one mode. But I
don't know how to do this, and I haven't actually used Parinfer, so maybe
there's nothing to worry about.

~~~
rcthompson
It looks like one of the modes is mainly intended for fixing unmatched parens
before switching into the other mode.

------
daly
You've done great work. But speaking as an old lisp programmer it doesn't seem
useful.

Code, for me, is a means of communication with both the machine and the human
reader. The machine doesn't care about lisp indentation but code format
matters when trying to communicate with human readers. Communication with
future readers, including yourself, is what makes programs maintainable.

Pretty-printing is fine since the code format follows a standard, which makes
it easy to ignore and easy to read, generally a "good thing". On the other
hand, there is information in the choice of code format, similar to the
information found in a poetry format.

Properly indented code is "good code" and, over time, is likely to be what
flows from your fingers regardless of the editor. But excellent code is an art
form that requires thought. Lisp is a language where indentation is
meaningless but shape communicates. It is not something to delegate to an
editor.

------
abc_lisper
Emacs people, I want this.

~~~
alphapapa
[https://elpa.gnu.org/packages/adjust-
parens.html](https://elpa.gnu.org/packages/adjust-parens.html)

------
jegutman
I would even find this useful when writing python and defining nested
dictionaries and lists.

------
sbochins
Wow, this is really something. I never really thought about it and assumed
that paredit would be as good as it gets. But, this seems to show that is not
the case. I really need to check this out.

------
ksmithbaylor
Wow! This is really impressive! I can't wait until someone ports this to vim.
I might even take a stab at it myself. Great work!

------
lewisl9029
I'm curious about this point from the article:

> And alternative syntaxes (Lisps without parens) have faultered since they
> sacrifice some of Lisp's power that seasoned users aren't willing to part
> with.

Could Elixer be an example of one of these Lisps without parens? [1] Can
anyone give an example of what kinds of Lisp powers are sacrificed in a syntax
where the parens are implicit rather than explicit? I can't think of anything
off the top of my head.

It seems to me that Parinfer, when working as intended, almost turns a regular
Lisp _into_ one of these parenless Lisps, by performing parens manipulation
for us, which in turn allows us to treat whatever Lisp we're working with as
if it was a parenless Lisp.

[1]: [https://blog.8thlight.com/patrick-
gombert/2013/11/26/lispy-e...](https://blog.8thlight.com/patrick-
gombert/2013/11/26/lispy-elixir.html)

~~~
roryokane
I think the author is talking about minor variations on Lisp syntax that
include indentation-sensitivity, not about different languages that are
homoiconic. Such variant syntaxes include i-expressions
([http://srfi.schemers.org/srfi-49/srfi-49.html](http://srfi.schemers.org/srfi-49/srfi-49.html))
and sweet-expressions
([http://readable.sourceforge.net/](http://readable.sourceforge.net/)), which
the author mentions in the Acknowledgements. For example, here are some sweet-
expressions:

    
    
      define factorial(n)
        if {n <= 1}
          1
          * n factorial(- n 1)
    

This desugars to:

    
    
      (define (factorial n)
        (if (<= n 1)
          1
          (* n (factorial (- n 1)))))
    

I think the Lisp power that the author is referring to is homoiconicity, and
the corresponding ease of writing macros. For example, i-expressions sacrifice
homoiconicity in some contexts.

But the author seems to have overlooked that sweet-expressions, which were
developed later, do not sacrifice homoiconicity. A human reader can still
easily understand the AST from reading sweet-expression syntax. The real
reasons that sweet-expressions have faltered relate more to the increased
difficulty of collaborating with other developers, the lack of editor plugins
that highlight sweet-expressions correctly, and incompatibility with Clojure
syntax.

------
tmalsburg2
Eh, not sure I like it. For this to work, they have to make the indentation
part of the syntax. Otherwise stuff is way too ambiguous to infer the correct
parenthesis. A result is that the programmer has to be intimately familiar
with how the inference works and what the syntax of indentation is. I feel
that this just shifts cognitive load from managing parenthesis to managing
indentation and inference. From the documentation:

    
    
      The rules for what happens when inserting/deleting parens must be
      learned. Also, the case necessitating a "Paren Mode" comes at the
      cost of forcing the user to understand when and how to switch
      editing modes.
      
      Also, the preprocessor step performed when opening files will cause
      more formatting-related changes in your commit history when
      collaborating with others not using Parinfer.

~~~
chrisoakman

      they have to make the indentation part of the syntax
    

The syntax remains unchanged. Parinfer only "infers" the structure of your
code based on the same indentation that you typically would do anyway as part
of formatting your code to be readable.

    
    
      shifts cognitive load from managing parenthesis to managing indentation
    

Yes, exactly :)

------
sagarjauhari
Love the fact that it is written in Clojure

------
dTal
In the example:

    
    
      (foo [1 2 3
            4 5 6
            7 8 9])
    

How does one change it to:

    
    
      (foo [1 2 3]
           [4 5 6]
           [7 8 9])
    

?

edit: Ah, it works if you put all the forms on one line. I suppose it would be
considered non-standard to indent that particular syntax in such a way.

~~~
chrisoakman
You could also just put a "[" in front of "4" and "7".

Parinfer will infer the closing brackets.

~~~
dTal
That yeilds

    
    
      (foo [1 2 3
           [4 5 6]
           [7 8 9]])

------
mark_l_watson
A beautifully written web site. It made me happy just to look at it.

I haven't been doing much Clojure or Common Lisp development this year but I
am putting Parinfer on my look-at list.

------
zeckalpha
If I go to lisp for day to day work, I plan on using the syntax from SRFI-110
to encourage other people to work with me:
[http://srfi.schemers.org/srfi-110/srfi-110.html](http://srfi.schemers.org/srfi-110/srfi-110.html)

Even if the code at rest is in S-expression syntax, editing it in this syntax
would be slick.

------
oberstein
Very neat demo, I hope it gets integrated into the major Clojure IDEs soon as
it could make the onboarding experience smoother. I'm happy with just vim
though -- I never liked the fashion of editors inserting/deleting/replacing
non-whitespace characters (even that took a while to get used to) when they
think they can to 'help' me.

------
michaelmior
This is great! I would love to see similar functionality integrated into Dr.
Racket.

------
nickpsecurity
Great improvement on dealing with the parentheses!

------
cabalamat
Is there a version of this site without the distracting animated text, so I
can see what is going on?

~~~
coldtea
The main way to see what's going is is the animated text -- because what's
going on is live re-indenting of code.

------
hulkaad
I look at it and keep wondering why it hasn't always been this way.

------
titanomachy
What tools did you use to make the documentation? It's excellent.

------
dopamean
Man I wish the paren management stuff for vim didn't suck.

------
incepted
This would make `git diff` an absolute nightmare.

~~~
chrisoakman
I can speak to this as I've been using Parinfer on existing code for the last
week or so.

There can potentially be a "big diff" when you run Paren Mode on a file for
the first time, but that's typically a one-time event. The beauty of Parinfer
is that most Lisp code already uses these conventions.

Once you start using Parinfer's Indent Mode regularly it feels crazy to write
or edit lisp without it.

One of my plans for atom-parinfer[0] is to have a comment flag in a file that
will automatically signal "use Parinfer on this file". Similar to JSHint
inline configurations [1].

[0] [https://github.com/oakmac/atom-parinfer](https://github.com/oakmac/atom-
parinfer) [1] [http://jshint.com/docs/#inline-
configuration](http://jshint.com/docs/#inline-configuration)

------
yarrel
As an aside, I love the lego diagrams.

------
jonathaneunice
Automagical.

------
Sevzinn
Made just good enough to get a job Rejection

