Hacker News new | past | comments | ask | show | jobs | submit login
Honu: Syntactic Extension for Algebraic Notation (2012) [pdf] (utah.edu)
71 points by goranmoomin on Dec 6, 2019 | hide | past | favorite | 61 comments



As a lover of Lisp, I find these sorts of initiatives really depressing and frustrating. They seem to come up over and over again, where someone has the bright idea to make Lisp less Lisp-like, often by getting rid of all the parenthesis and making the language more Algol-like.

But parenthesis are, for me, one of the greatest things about Lisp. They make understanding, reasoning about, editing, and manipulating the code really easy, and add a consistency to the language which I really appreciate when compared to mismash of arbitrary, inconsistent delimiters in other languages.

This leads me to adopt a "you'll have to pry my parenthesis from my cold, dead fingers" attitude when I see suggestions like these. Please, just keep Lisp Lisp.


The funny thing is that I'd even prefer s-expressions for a "static" language with tons of compile-time zero-cost abstractions. Having to remember less syntax allows me to focus on the semantics, which is what matters to me.


The title of the Hacker News post ("Honu: Lisp with Algol Syntax") is misleading. The real title of the paper is "Honu: Syntactic Extension for Algebraic Notation through Enforestation". It's a paper showing how to implement syntax-case in a language with infix notation.


We've changed the title from "Honu: Lisp with Algol Syntax", in accordance with the site guidelines.

https://news.ycombinator.com/newsguidelines.html


I would agree, I love the structure s-expressions bring, especially using smartparens in Emacs. At the same time I really enjoy some of the deviations from pure s-expressions that Clojure has made (like hash literals, using vectors for function arguments, using position instead of parentheses in binding forms). This suggests to me that there might be a sweet spot between Algol and s-expressions.

I'm quite interested to see where the whole Racket 2 thing goes in that regard. For example, the examples here all look quite clean to me: https://github.com/mflatt/rhombus-brainstorming/blob/shrubbe...


I feel like Clojure's deviation from S expression still somewhat follows the spirit of Lisp at least; the first-class hashes and vectors still correspond to native data structures and as a result, the syntax reflects that. In some ways, I feel like Clojure's embrace of this almost follows the Lisp formula better than something like, for example, Chicken Scheme, which by default does scheme-style hashes that don't correspond directly to syntax.


I've also grown to love s-expressions, but even Common Lisp deviates from pure s-expressions in places ( #( ... ) array syntax, for example), and the mechanism is customizable via reader macros.

Some examples:

https://gist.github.com/chaitanyagupta/9324402

https://lisp-lang.org/wiki/article/reader-macros


Co-author Mathew Flatt is part of Racket's core team. Racket is a language development platform. Honu is written in Racket.

https://github.com/racket/honu/tree/master

Honu isn't a Lisp replacement. It's an academic research product.


i think it's more than just an academic research product. all of these efforts and more are the core racket team looking into replacing racket's syntax, in particular its parenthetical syntax. they have explicitly stated that they want to and will do so.

so honu isn't a lisp replacement, but it is one evolution and inspiration for getting rid of the lispy syntax of racket.


Honu is from 2012. Its primary purpose at the time was to explore syntactic macros in languages with infix expressions. It was not explicitly about identifying alternatives for Racket's future development, as far as I've been made aware. (I've talked about this paper with Matthew before.)

It's also worth pointing out that Racket isn't going anywhere; it will always have parenthetical syntax. The future project being worked on is now called Rhombus to disambiguate it from Racket's past. Racket will not be replaced by Rhombus.


Dunno, I feel that both Dylan and Julia are somehow good enough approaches.

Dylan failed because Apple losed interest, if they had Swift like motivation back then, it might have turned out differently.

Julia, lets see how it goes.


This sounds like an admission that the only way a programming language survives is when a corporation decides we should use it.

Given what I’ve seen in the past 25 years, I’m not sure you’re wrong.


Because programming languages are just software products like anything else, where the customer base are the developers.

So you need an use case to keep your product on the market (programming language).

Corporations are the ones with deep enough pockets to push those use cases, even if initally they don't caught on.

For example, if Dart was a FOSS project it would have died by now, instead Google can keep throwing money at Flutter until Dart actually picks up.


I think I repeat basically this every time the topic comes up. Marketing works to build a market for products, but is expensive.

Yes, there are grass roots marketing that gets some languages adopted. They seem to be the exceptions proving the rule.

So much of the technical chase for getting a successful language feels jarring compared to the momentum of languages that are succeeding.


Programming languages have large network effects, so just being able to throw a lot of developers at a language has a major effect.


you don't remember M-expressions?

>S-expressions were originally intended only for data to be manipulated by M-expressions, but the first implementation of Lisp was an interpreter of S-expression encodings of M-expressions, and Lisp programmers soon became accustomed to using S-expressions for both code and data.

https://en.wikipedia.org/wiki/S-expression#Use_in_Lisp

now, i won't say that i don't enjoy S-expressions, because i do. i do think there is some value in creating ..less intimidating front-ends to lisp-fam languages. one of the things that i love about racket is their focus on alternative syntax. i love that. everyone knows that lisp is capable of creating new syntax, but most of the time that ability is not used to its fullest extent. some standardization is needed, but it would be really cool to have a way to create compilers for arbitrary languages using advanced syntax macros. especially if the lisp representation is callable from other lisp programs AND other languages. JVM and BEAM have this, why not lisp?

personally i'm very interested in creating an elm compiler using chicken scheme. it is a little bit outside of the C-like comfort zone, but it is made to be very beginner friendly which i appreciate.


> it would be really cool to have a way to create compilers for arbitrary languages using advanced syntax macros. especially if the lisp representation is callable from other lisp programs AND other languages

You are perfectly describing Racket here.


oh yeah i'm a big fan of racket. i'd prefer something that's AOT, but maybe i can make an AOT racket :thinking:


I don't think people already on the lisp boat are the target audience of these things.


Agreed. And the phenomenon is not exactly new. I played around with CGOL about 35 years ago, and although I was already a fan of s-expression syntax, I still thought CGOL was great for certain applications. It's cool to see Vaughan Pratt (who designed CGOL in 1973) in the bibliography of this article.

S-expression syntax seems to be very divisive. Every time I have introduced that syntax into various projects (since it's such an easy and clear solution for domain-specific languages) for the past couple of decades, I have met resistance because so many people are repelled by all those parentheses.


They may be depressing and frustrating, but I think they are necessary. They answer a "what-if" that most people learning Lisp will ask themselves at some point.

Eg: I was never into the Lisp usage of parents until I saw some of the alternatives (I still prefer M-expressions though, but that's just personal taste).


How does this compare to Scheme sweet-expressions https://srfi.schemers.org/srfi-110/ and curly-infix https://srfi.schemers.org/srfi-105/ ?


After seeing this, I know why I prefer Lisp with its conventional non-syntax over this mess.


You're not the first. Peter Norvig wrote:

> Python does have access to the abstract syntax tree of programs, but this is not for the faint of heart. On the plus side, the modules are easy to understand, and with five minutes and five lines of code I was able to get this:

>>> parse("2 + 2") ['eval_input', ['testlist', ['test', ['and_test', ['not_test', ['comparison', ['expr', ['xor_expr', ['and_expr', ['shift_expr', ['arith_expr', ['term', ['factor', ['power', ['atom', [2, '2']]]]], [14, '+'], ['term', ['factor', ['power', ['atom', [2, '2']]]]]]]]]]]]]]], [4, ''], [0, '']]

> This was rather a disapointment to me. The Lisp parse of the equivalent expression is (+ 2 2). It seems that only a real expert would want to manipulate Python parse trees, whereas Lisp parse trees are simple for anyone to use.

Honu appears to have a ton of complexity just to be able to support writing "a+b".

[1]: https://norvig.com/python-lisp.html


First, it's been nearly two decades since Norvig's examination, and Python grew the `ast` module in the stdlib, which improves things greatly:

    >>> ast.dump(ast.parse('2 + 2'))
    'Module(body=[Expr(value=BinOp(left=Num(n=2), op=Add(), right=Num(n=2)))])'
Not perfect but much improved. There are two reasons why Python ASTs are richer than Lisp/Scheme ASTs. First, Python simply has a richer syntax and semantics. (Too rich IMO!) Second, Python ASTs contain extra annotations, like line information, which are indispensable for production-quality code transformation; I assume that production-quality Lisp parsers do the same thing one way or another.

Honu does indeed have a lot of complexity, but I don't think that it's bad; I think that it's table stakes for offsides-indentation layouts. By merely having its primary goal as a goal, Honu must be complex.


> Python simply has a richer syntax and semantics. (Too rich IMO!)

This is the core of the issue. If there were a Zen of Lisp, it might just be two lines extracted from the Zen of Python:

  Simple is better than complex.
  Complex is better than complicated.
The reason why s-expressions are so great is because that simplicity makes metaprogramming manageable. I'm curious about how the new Racket syntax works out, but also mildly skeptical. . . I fear that a "lisp" where the ASTs don't look exactly like the text I see in the editor is a lisp that has had its soul amputated.


> I'm curious about how the new Racket syntax works out, but also mildly skeptical. . . I fear that a "lisp" where the ASTs don't look exactly like the text I see in the editor is a lisp that has had its soul amputated.

FWIW I talked to Matthias Felleisen about this to some degree this past summer. I asked whether he felt homoiconicity is important, and he essentially said that no, it wasn't particularly important so long as whatever system you arrive at has a sufficiently well-considered design. (Though he couldn't/wouldn't tell me which aspects of syntax he felt are important to designing a metaprogramming system as complete as Racket's.)

It's also worth noting that Racket's syntax is not changing. Matthew has said that he regrets calling his proposed project "Racket 2" because he never really intended it to subsume Racket, but rather to be a sister-project. The new name is Rhombus, and his proposed syntax uses what he calls "shrubbery notation" [0].

[0] https://github.com/mflatt/rhombus-brainstorming/blob/shrubbe...


Indeed some of the lisp soul would be amputated in those languages, but that's usually done as a deliberate design and not as an unfortunate restriction. Sort-of-homoiconic languages (as in featuring a direct mapping between it's surface syntax and homoiconic representation, and support for manipulating the surface directly through quote/unquote) like Julia and Elixir (plus Nim, D, Rust, Scala and other languages with powerful macros) don't want macros to be your first thought when facing an issue, nor the second or third thought.

In most Lisp your custom syntax is as first class as any standard syntax, which could lead to maintenance problems when every developer in the group could easily end up making his own poorly documented language to solve (even if perhaps optimally) a part of the problem. But in those other languages there is a first class way of doing something (a standard way of looping, standard way of conditional tests...), and macros are either discouraged or have to be annotated every time it's called to warn the user that the syntax ahead is not conventional. And macros are really there for the last resource when the surface syntax is really not expressive enough for the problem, and by then they'll be worth even when dealing with the AST in a much less convenient way.


If you're interested, I suggest looking at "readable Lisp". Details here: https://readable.sourceforge.io/


i think you may have just broken sourceforge with this link...

> SourceForge project websites are currently experiencing abnormally high levels of traffic. Our support staff has been notified. Please check back in a few minutes. We thank you for your patience.


Fortunately or unfortunately, it seems like Racket is going to move to this new syntax, as Matthew Flatt is a core contributor to Racket. I’ll hold judgment until I see it, but I’m skeptical, to say the least.


got any links about this? I'm not sure what the right search incantation is. Too much talk in Racket about new / experimental syntax. ;)



LOGO is already Lisp without parentheses and with infix notation. Recently, doing exercises in LOGO with my kid,he asked: can we put some parentheses around the functions for better clearness? I said: my son, you just discovered Lisp!


Lisp parentheses are not just for grouping. In Lisp, x is not the same as (x). That confused me a lot at first because it's rarely explicitly stated.


Yes, LOGO already has all the list evaluating like the real Lisp.

? run `[print ,[sum 2 3]]

5

But also a very sophisticated way to define functions with many default and within even optional parameters without the need of parentheses. From UCBLogo info:

This version of Logo allows variable numbers of inputs to a procedure. After the procedure name come four kinds of things, in this order:

            1.   0 or more REQUIRED inputs    :FOO :FROBOZZ                                    

            2.   0 or more OPTIONAL inputs    [:BAZ 87] [:THINGO 5+9]                          

            3.   0 or 1 REST input            [:GARPLY]                                        

            4.   0 or 1 DEFAULT number        5


Can you elaborate or maybe link to a resource that explains this? This seems to be missing piece of the puzzle for me.


When you put parentheses around an atom, lisp sees that as a function call.

Say `x` is a function which adds one to a number.

Say `y` is an atom which is not a function—perhaps a list: `(list 1 2 3)`.

If you write just `x`, no parens, you point to the function without executing it. If you do this at your interpreter it will tell you that the value of `x` is “function something something.”

This is important because it’s what makes it possible to pass functions as arguments, and write things like `(map x y)`. Here `x` is not evaluated in place; it’s called later by the `map` function, once for each element of the list, resulting in `(list 2 3 4)`.

Now if you put parentheses around `x`, lisp will try to execute it on the spot. `(x 1)` is what other languages would call `x(1)`, and will return `2`.

You have to be a little careful about this because it means that if you put parens around `y`, which isn’t a function, you’ll get an error. This is why lisp has the ‘quote’ operator, which means “take the next atom as literal data and don’t try to execute it.” So `(quote (1 2 3))` will return `(1 2 3)`. This can also be written `’(1 2 3)`—which is nice, because this is effectively equivalent to `(list 1 2 3)`, and could have saved me some typing when I defined `y` earlier.

e: typo ; also, sorry for the ugly syntax, I forgot there was no inline code formatting on HN


Hold up. X in the above is a symbol. Symbols may be bound to a function or they may not. But x here is always a symbol.

When you write (x) in the Lisp REPL, Lisp will assume you intend to call a function bound to x because x appears first in the list. If you instead wrote (y x), Lisp would never assume x was a function. If the first thing in a list is not bound to a function, Lisp will throw an error.

When you write '(x) [note leading quotation mark], Lisp will simply construct a list that contains the symbol x as its only element.

In my explanation above, I didn't make the distinction about the quotation mark because I assumed the questioner was asking about data structures. Lisp will print "the list containing x" without the quotation mark. But if that's what you mean when you type it, you have to explicitly type the quotation mark.


Yes, this is a good clarification!


The inline syntax makes it clearer what you mean. After some time markdown reads as text. Thank you!


One is a function, one is a call to that function. I think this is just hard to grasp because in other languages there's often not occasion to use a function pointer. But compare these two code snippets in Python and Scheme:

    >>> def add_one(x):
    ...     return x + 1
    ... 
    >>> add_one
    <function add_one at 0x7f7504bf2050>
    >>> add_one(1)
    2
    >>> list(map(add_one, [1,2,3]))
    [2, 3, 4]
That's the same as:

    => (define (add-one x)
         (+ x 1))
    add-one

    => add-one
    #[compound-procedure add-one]
    
    => (add-one 1)
    2
    
    1 ]=> (map add-one (list 1 2 3))
    (2 3 4)


Writing "x" in Lisp is like writing "x" in JavaScript.

Writing "(x)" in Lisp is like writing "x()" in JavaScript, that is executing the function which was stored in the variable x.


Strictly, this isn't right either. For example,

    (defun Foo (x) ...
That is not calling a function x. Similarly,

    (let ((x 4))...
Also not calling a function.

It is just that, in lisp, you start a list of data with paren. In other languages, you activate a different part of the parser. It so happens that the default thing the evaluator does to a list is to apply it to a function given in the first spot.


x means "the symbol whose name is x". Symbols are first-class data structures in Lisp; they are basically C structs with a predefined structure, one of whose fields is called "name" which contains a string. (The other fields are unimportant for this discussion.) Symbols are looked up by their name, so there can only be one symbol whose name is "x" (not completely true but true enough for this discussion).

(x) means "a list that contains the symbol whose name is x." Lists are also first-class objects in Lisp, and they are built using cons cells. A cons cell is itself a struct with precisely two fields: a CAR and a CDR. And you can put anything in either field, including a pointer to another cons cell. The x symbol in this example would be pointed to by the CAR of a cons cell, and the symbol NIL (a special global symbol) would be pointed to by its CDR to indicate "the end of the list."

So (x) means a struct whose CAR contains a pointer to the symbol named "x" and whose CDR contains a pointer to the symbol named NIL. Otherwise known as a list with one element.


Honu's real trick is supporting Racket's syntax-case in an infix syntax.


I like the folks involved with Honu and new language initiatives are always great, but to me this reads as "Honu's real trick is supporting something I don't want, using something I don't want"


Well, given that it's meant to be language research, I think there's a ton of value in it. We already have a bunch of popular existing languages with infix syntax, and Honu shows how a powerful hygienic macro system can be applied to those.

You may not want a powerful macro system in those languages, but I personally think it would far preferable to the existing mess of annotations and build-system hacks to generate code.


This needs an upvote.


After using Clojure for awhile, I think the only syntax I would prefer it is Ruby’s.


Smalltalk?


Haven’t taken a deep look, but the first examples look surprisingly clean.


Related (by not mentioned?): https://en.wikipedia.org/wiki/M-expression


It's not mentioned because the paper isn't actually about Lisp with Algol Syntax. It's about how to add syntax-case to a language with Algol Syntax.



It looks like javascript syntax.


This is a reasonable observation, JavaScript uses a syntax derived from Algol, via C, with some language features derived from Scheme.

I understand some of the upthread scepticism of the role that this may play in racket moving forward, but while people may have reservations about some of the Wut features in JavaScript, it has undeniably been a very successful language


it's been a successful language because the web is effing amazing and it's literally your only choice if you want any functionality in a web page.

If the original Scheme-like plan for JS had of been used then you'd be saying exactly the same thing about a Scheme.

JS didn't succeed because it was "good" in _any_ way. It succeeded because it was not horrendous and it was/is _the only choice_.


I mostly agree with you. I think however that it was "the only choice" because it was good enough. It's worth remembering that there was a non trivial time period in which there was a single dominant browser platform which supported alternatives: VBScript was supported through IE10, and is still available in IE11; ActiveX could be used to allow just about any scripting language.

Even now it's fairly straightforward to have JavaScript be the output of a toolchain, but not necessarily the input


And a Lamborghini looks like a soapbox cart.


Could you please stop posting unsubstantive comments to Hacker News? You've been doing it quite a bit and we're trying for something a bit better than that here.

https://news.ycombinator.com/newsguidelines.html




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: