Honu: Syntactic Extension for Algebraic Notation (2012) [pdf]
cs.utah.edu
cs.utah.edu
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.
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...
Some examples:
https://github.com/racket/honu/tree/master
Honu isn't a Lisp replacement. It's an academic research product.
so honu isn't a lisp replacement, but it is one evolution and inspiration for getting rid of the lispy syntax of racket.
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.
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.
Given what I’ve seen in the past 25 years, I’m not sure you’re wrong.
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.
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.
>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.
You are perfectly describing Racket here.
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.
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).
> 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".
>>> 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.
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.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...
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.
> 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.
? 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 5Say `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
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.
>>> 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, that is executing the function which was stored in the variable x.
(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 "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.
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.
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
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_.
Even now it's fairly straightforward to have JavaScript be the output of a toolchain, but not necessarily the input