Readable Lisp S-expressions Project (2013)
readable.sourceforge.io
readable.sourceforge.io
FWIW, there've been many attempts in Scheme (and Racket), including one by the core Racket developers (Honu [1]), but they've never taken off. New people decide to use parentheses.
There's a phenomenon with Lisps, in which, speaking loosely, half the people who first see a Lisp think the parentheses are a problem, and immediately start to propose "solutions" for "acceptance". But then they try the Lisp with the parentheses, in an editor with at least simple indenting support, and they very quickly get used to it, and maybe even value it. (A small percentage of them will still put each close-paren on its own line, for a substantially longer period, but it seems even most of them will quickly learn to snuggle close-parens and possibly to use fancier editor commands for movement and editing based on expressions rather than lines.)
Programmers seem more malleable than we might think. I'm not as sure about those mathematicians or scientists who try to minimize their time learning programming, or who are necessarily working with hairy expressions in LaTeX and don't want to have to keep translating. For possible use of those people, there are a number of efforts to permit expressions to be in more familiar infix order [2], and to support familiar TeX math notation in the Racket-based typesetting/documentation system [3]. (And if you don't like those, Racket will let programmers plug in your own reader or `#lang` to make another attempt at this atop Scheme.)
[1] https://docs.racket-lang.org/honu/Examples.html
[2] https://docs.racket-lang.org/guide/hash-reader.html#(part._r...
>> There've been so many attempts at removing the parentheses from Lisp, to make it look more like C...
I think it's less about looking like C (or other) it's about looking like normal Math: (f 1) vs f(1). The above plugin solves this. Maybe the neoteric feature is the most important of the three extensions.But then again `unnecessary` braces are nothing to torture malleable newcomers with, they are just a thing to get rid of.
The best previous attempt IMO was Egil Möller's I-expressions in 2003 (https://srfi.schemers.org/srfi-49/srfi-49.html). These Sweet-Expressions in the OP I think were a regression on I-Expressions.
My attempt (~2013-present) is called Tree Notation. http://treenotation.org/. This is the first time that we've got S-Expressions without parentheses and are using that for tremendous benefit (better concatenation, easy ad hoc parsers, visual programming and program synthesis being some of the main benefits).
Here are a few sample languages: http://treenotation.org/sandbox/build/
> but they've never taken off.
It's going to happen finally with Tree Notation. We're starting to see strong network effects at last. The missing ingredient was strong tooling/design/and tools that leverage the lack of parentheses to huge benefit.
OP claims to be backwards compatible with Clisp, that's not a goal of yours, right?
So a list in treenotation would be written:
list a
1
2
3
4
? Could become a bit verbose at times, or does treenotation have a mechanism for tables like data={
header:['brand','make','price],
rows:[
['mercedes','X1',1000] ,
['porsche','Z7',2000],
]
}
are triples always parsed like this: ?description A package
(description (A package)) vs ((description A) package)
Does treenotation differentiate between strings and data/ints?
Correct, not a goal of mine. If someone wanted to create a Tree Language that compiles to CLisp, they could define it in our Grammar Language (http://treenotation.org/sandbox/build/ -- you write your grammar on the left, and source code in your new language on the right).
> So a list in treenotation would be written:
list a
1
2
3
4
The base spec for Tree Notation is very simple. YI = "\n" // New lines separate nodes
XI = " " // Increasing indent to denote parent/child relationship
interface TreeNode {
parent: &TreeNode
children: TreeNode[]
line: string
}
In practice, we generally break "line" into "words: string[]". So you can represent atoms in a list as either words or nodes, depending on how you design your Tree Language.> Could become a bit verbose at times, or does treenotation have a mechanism for tables like
Sort of. We have a software coming out called TreeBase that is a plain text, git backed database that stores records in Tree Notation. In there we have Tree Languages for dealing with SSV/TSV/CSV/etc tables. I'm not 100% happy with the current implementation--going to be putting pattern matching into our Tree Grammar Language which might solve it--but I bet within a month or two we'll have a great solution for that with strong type checking, etc.
> Does treenotation differentiate between strings and data/ints?
The base Tree notation is very simple, as the spec above shows. But we created a Tree Language, built upon Tree Notation, wherein you write grammars to create new languages. In those grammar files you can define ints, strings, arrays, etc. The link above has examples with all of those.
It seems mostly similar, except you hide the parenthesis inference.
My long term plan is to get a lot of resources dedicated to this initiative, and even though I think it's possible you could accomplish the same thing with S-Expressions, I think it's simpler and cheaper by a significant amount to just drop the parens.
I think the number of folks who are comfortable with parens (I'd count myself in that category) is a lot smaller than the folks who don't like parens or don't have a preference.
I did think of making an Emacs plugin that could hide the parens when using pareinfer. They'd still be in the code, say for others who choose to use a different editing style, but hidden to me.
Another aspect for Lisps is that single line code blocks at the REPL are quite useful. Not all shell have good support for multi-line, so being able to inline tree like code does come in handy there.
I've also personally found the parens sometimes help with readability, indent can be hard to tell where the block starts and ends sometimes in deep nested scenarios which is more common in immutable expression only languages.
Lastly, I'd say copy/pasting can be annoying in whitespace sensitive syntaxes. Pareinfer suffer from this issue as well.
But I'm biased, I like the parens and love S-expressions. I'm one of those who started like everyone else hating them, trying to look for alternatives, but one month later of using Clojure at work it all changed, my brain adapted and now I find S-expressions superior in all ways to all other syntaxes I've ever used. I even find myself writing other language with S-expressions by accident. I often put the parenthesis before the method by accident now when going back to Java for example.
This is a really good point. I'm thinking of experimenting with a convention for doing inline trees using Tree Notation. In the code itself the delimiters are called YI (newline) and XI/ZI (space), so potentially could do something like this for the REPL: (set YI="|" ... "def square x|return|* x x "
> Lastly, I'd say copy/pasting can be annoying in whitespace sensitive syntaxes.
This is true. Things that minimize it in Tree Notation: no ambiguity with spaces/tabs/indent levels--one space is the only way to indent things; great syntax highlighters & automatic suggested fixes (I've got a todo to add autosuggested fix the common case where subsequent lines are de-dented after a paste); and the ubiquity of multi/vertical cursors in editors nowadays. Still, whether I'm using Sublime or VS Code or vim, indent can sometimes be an issue. I think it's just a matter of having all the grunt work done for the modes in these languages to ensure things work as you'd expect. I bet we get that all done by end of year at latest.
> my brain adapted and now I find S-expressions superior in all ways to all other syntaxes I've ever used.
I think largely the same way, except to me just swap S-Expressions with Tree Notation (which is just SE w/o parens). But that's only because the tooling is getting there now. When I look back at where things were in 2017, wow, Tree Notation was very hard to use back then.
It never took off. From the earliest days -- before video terminals were a thing, let alone video-terminal editors that could match parens and indent automatically -- people just preferred working with sexprs. I think it has to do with the fact that while mexprs are easier to read with prior ALGOL experience, sexprs are easier to transform, and to reason about their transformation.
I think Lisp is one of those things where, you either get it or you don't, and once you do get it parens are a non-issue. There's a fundamental leap of insight that requires enormous activation energy to achieve.
Being not as powerful (able to create any valid syntax due to more complex and rigid parsing rules) and trivial to manipulate code as data as s-expression is considered a feature, as it helps preventing the proliferation of language extensions while still allowing them when their benefits outweighs the costs. Even the main elixir metaprogramming book starts with saying: "the first rule of macros: don't use macros", and in Julia you have to attach a @ to call a macro to make it clear that the normal rules of the language do not apply ahead. Those languages also focus in providing everything the user needs without the use of macros, which are for more advanced purposes.
While people who use Lisp eventually see the advantages of s-expressions (or at least of writing more idiomatic code in those languages), being attractive to new users coming from the more popular languages is very important (and like clojure interoperate with java, it might be even easier to make a lisp that has perfect interoperability with Elixir for example, so both groups can coexist).
Although Julia goes further in other directions, just changing the surface syntax of s-expressions, like this proposal, doesn't really interfere with this feature. With alternate syntax, you can still write macros and stick to the code-as-data idea in pretty much the same way. In Lisps with a programmable reader (like Common Lisp and some Schemes), you can set up the reader to recognize something like foo[bar,baz] as an sexp, and once read, it'll be represented internally exactly as if (foo bar baz) had been read with the default reader. Then any macros and even code-walking code continue to work the same as before.
And while programmable reader macros allow Lisp to have any syntax you want, they go even further in producing heterogeneous environments in which not even the parsing rules are shared. But you can't just only know the algol/infix way of programming in Lisp since you'll have to see other people code and documentation, and when you use these alternate parsers/extensions other people have to understand the quirks of your unique parser/extension before understanding your code. Everyone using the exact same language is definitely a feature, and those languages (and clojure who dropped programmable reader macros and asks people to use macros more judiciously) have a point in their belief.
But as a curiosity, Julia also has it's own version of reader macros [1], which can be used to give it s-expression syntax [2].
[1] https://docs.julialang.org/en/v1/manual/metaprogramming/#Non...
f[1, 2, 3]
And we can index it Part[f[1, 2, 3], 1] == 1
Now what would the 0th element be? Part[f[1, 2, 3], 0] == f
It is “head” or what corresponds to the first element in cons lists.Also, as an appendix, there is no reason why you can’t have a “high-level” syntax and an abstract syntax which can both coexist. Mathematica has a syntax which can get quite cryptic (almost APL like at times), but it is possible to ask “freeze” expressions and print out the full m-expression form and thus inspect the structure of a function or similar.
Weirdly enough, even if a language isn't "lispy" I would still consider a expressions a point in it's favor.
I don't think it requires energy per se, because s-expressions aren't actually difficult at all. The biggest hurdle seems to be willingness to learn something new.
Every time somebody proposes to "fix" Lisp's syntax it reminds me of that scene. It sounds like a newbie Lisp programmer who hasn't yet experienced the thrill of seeing beyond the parentheses. And the mind-boggling compositional utility that comes from leaving them alone.
if 1:
<tab>if 0:
<tab><tab>print(1)
<spaces*n>print(2)
could print 2 if mixing tabs and spaces were allowed. was it ever? f(a b) == (f a b)
{a f b} == (f a b)
+ some edge cases {a b}=(a b) {a}=(a) {}=() etcWith good tooling this is not a problem. Try the demos here: http://treenotation.org/sandbox/build/. Make some indentation errors. It's very hard to ignore all the help the editor gives you.
> And elegant composition is impossible with infix notation unless -- you add parens again.
This is a valid criticism. I've seen some folks working on this though, and I expect we'll see a new elegant solution.
>> And elegant composition is impossible with infix notation unless -- you add parens again.
> This is a valid criticism.
Is it? What is an example? (One I could think of below seems solvable)I was thinking of "elegant composition" of subexpressions. Or, in other words, inline Trees/S-Expressions.
You'll note that most code people write in almost any language is prefix anyway. `foo(bar, baz)` isn't infix; it's prefix with the operator placed to the left of the paren.
Is elegant composition possible with prefix notation and some whitespace and indentation instead of parentheses everywhere?
I think it might be.
I parse Lisp similar ways and speed as Python. The indentation in Lisp code is works the the same way.
Q: How can you tell when you've reached Lisp Enlightenment?
A: The parentheses disappear.
–– Anonymous They are disturbing only people who don't program with Lisp a lot.
Which is about 99.9% of the population. It's good for Lispers that they can ignore braces, but why ignore them when you can just drop them (in many cases), and thereby increase it's chance of mass adaptation.BTW, this post was an outcrop of another article "Why Lisp Failed (2010)" https://news.ycombinator.com/item?id=20374782
Agreed! For decades some people have said parens in S-Expressions are useless.
But a key thing we're pushing, that's never been pushed before, is that the parens are worse than useless--they are harmful! They prevent easier program concatenation and ad hoc parser writing. And they make program synthesis, visual programming, and code analysis ~10x harder, for next to no gain.
I'm hoping we'll start to turn the tide and bring the 99.9% of the population into the Tree Notation world, and those LISPers that love their parens can remain happily in their (world).
Almost everything you've said above is not just untrue, but the opposite of the truth. As such, your statements require supporting evidence. Please cite some.
And who, by the way, is "we"?
There is also a big open source release coming soon—the largest database of programming languages and features in the world by a factor of at least 10x, more like 100x. Over 10k languages, over 1k columns.
We is the Tree Notation Lab. Will be announcing what that is and the funding for it later this month.
This assertion is patently false. Parens make it quite trivial to parse and render the underlying tree structuee rather obvious. Concatenating programs is just a matter of splicing a subtree into the target tree.
Here's a proof.
- In S-Expressions with parens, there are infinite ways to write any given abstract syntax tree.
- In Tree Notation, there is only 1 way to write any given abstract syntax tree.
If you change one letter, you get a different tree.
- Say I wrote programs in a Tree Language with a root node called "dumpToConsole" and I want to
rename all such nodes to "print". I can write a type-safe ad hoc parser that does
a simple search replace "/\ndumpToConsole/print/",
using Tree Notation. If it were in S-Expressions, I'd have to use a full parser.There is no need to change Lisp into different language because they already exist. Trying to get people who don't want to learn Lisp to learn Lisp is pointless.
It is the unambiguous notation enabled by the parentheses that allow lisp to process code as data and construct programs on the fly.
IMHO, those who suggest eliminating the parentheses don't yet understand lisp. Trying to make lisp more widely acceptable by eliminating them is throwing the baby out with the bathwater.
IMHO, those who suggest eliminating the parentheses don't yet understand lisp.
What's wrong with the three equivalence transformations of OP? It's the same language, only that newline/indent serve as hidden parentheses. f(a b) == (f a b)
{a f b} == (f a b)
f
a b ≛ (f (a b))Objectively:
1. I can't look at a line and tell if I have a complete expression. I must look at the following line and compare its indention level to this line. I may also need to look at the previous line(s) to see if we've switched to infix unexpectedly.
2. Indention means your programs will be bigger in physical area (meaning more scrolling).
Pragmatically:
3. People are unable to reliably copy/paste indention-languages into many web forums (like HN and Reddit) making it harder for them to get the help they need when starting out.
4. It's slower and more complex to parse, making build/compile times slower.
Subjectively: It just looks worse.
Autoindent and paren matching is currently a basic feature of virtually all mainstream text editors. Therefore, picking up any random mainstream text editor is already enough to address your concerns.
Without that, why am I using Lisp? If I just want a reasonably high-level language but don't care about macros (or other features that rely on seeing the actual structure clearly), why wouldn't I just use Ruby/Python/Smalltalk? They're pretty good, too.
I can almost guarantee it's going to be more pleasant, down the road, using one of those languages as it's intended to be used, rather than trying to contort Lisp this way. Getting an answer to a Lisp question on StackOverflow is hard enough with standard syntax. No Lisp programmer is going to want to help with a program written like this.
In fact, I'm missing an equivalent to paredit now when I have to go back and code Java/JavaScript :)
If you know Lisp you will instinctively convert any syntax back to S-expressions anyway because that's how things will eventually work anyway and because you can't think of Lisp macros otherwise. So, the syntax becomes a burden, and thus you settle for the minimalist, easiest way to denote trees.
Parentheses.
Lots of talk about removing the parenthesis, using things like Tree-Notation and such.
Really, all I can say is that you can pry the parens from my cold, dead, cantankerous hands. :P
Maybe it's because I've been playing with Lisp, in one form or another, on and off for several years.
But I never found the parens really all that bad.
It's no problem at all as long as you have basic paren matching support in whatever editor you use.
And if you happen to use an editor that supports the structured manipulation of S-expressions, well, it's a pretty neat experience.
I think the only time the parens bother me is if I print source code out. But even then, there are clear conventions on how Lisp source should be laid out and indented, so you adapt pretty quick, even in the "worst case."
shrug
Readable is a lisp plugin which allows you to drop superfluous braces and use function and infix notation:
(define (factorial n)
(if (<= n 1)
1
(* n (factorial (- n 1)))))
can now be written as define factorial(n)
if {n <= 1}
1
{n * factorial{n - 1}}
One more proof that the original lisp is a god like language. And now even beautiful to look at.Paul Graham may agree with you! Graham found a guy on slashdot with the same funny notion:
> "I have heard more than one LISP advocate state such subjective comments as, "LISP is the most powerful and elegant programming language in the world" and expect such comments to be taken as objective truth. I have never heard a Java, C++, C, Perl, or Python advocate make the same claim about their own language of choice."
> - A guy on Slashdot. What theory fits this data?
significant space? (a b(c d)) != (a b (c d)) ?
That would be acceptable (but break backward-compability?)
PS: Thanks for fixing the title but in this case it's much less expressive than the original.
On HN, being the submitter of an article conveys no special rights over the content, such as how to frame it for readers. If you want to express your opinion, the place for that is in a comment; then your view is the same level as other users'. Titles are by far the biggest influence on threads, so this rule is a big deal.
A more conventional syntax might look more appealing for non-Lispers, but I don't think it will make a big difference for adoption. If you want a more conventional-looking language, there are already plenty to choose from.
It's a shame lisp doesn't allow a simple text-based preprocessor.
text-based preprocessor.
Maybe 'asdf'? IDK, that's what this plugin uses:(require "asdf") (asdf:load-system :readable) (readable:enable-sweet)
...
XML has attributes which S-exprs don't. This can be solved by convention (as SXML does) and doesn't really represent a difference in expressibility.
S-exprs have quote and unquote which XML doesn't. This is a fundamental difference and makes it hard to convert Lisp code that mixes code and data representations.