| Ah, yeah, I forgot to mention this explicitly earlier, although I had this in mind when I wrote "s-exps based" syntaxes and when I wrote this part: > and you can add whatever syntax sugar you need in a couple of lines of a macro. In other words, Lisps give programmers tools for making their code as readable as they want (and are able to) But, I didn't provide any examples, so big thanks for filling in this gap! :) --- > The obvious reason is that we would find it irksome to be writing (add ...) and (mul ...). Yet, in Scheme, we have `add1` and `sub1`, `expt` instead of `^` and so on. The former two are written `1+` and `1-`, respectively, in Emacs Lisp and Common Lisp. What I want to say is that evidently there are people who don't mind the longer names ;) (Personally, I don't mind either, I think) > Lisp can have notations, and they can be had without disturbing the Lisp syntax. Plus, you can introduce them without modifying the language implementation, ie. you can add notations to Lisp even if you're just a language user and not the language developer. This is impossible in 99% of other languages. > Notations that are related to major program organization have payoff. I'm seriously wondering if I could fit this on a T-shirt! :) It's very true, and when I wrote about "incredibly readable and to the point" code I was thinking exactly about crafting and using special notations and internal DSLs. The payoff of a well thought-out notation can be tremendous. > In TXR Lisp there is relatively small set of new notations I don't know about that :D At least TXR Lisp has more syntactic conveniences than Scheme, CL, raw Racket and Emacs Lisp. I think only Clojure comes close - at least object slots' access is written in a similar way. But I think there's no syntactic sugar for slices, nor for string interpolation in Clojure. The syntax for "rest args" is similar in Schemes - `(define (f . args) ...)` - but they don't have a convenient way of calling a function which accepts a variable number of arguments, you need to use `apply` for that. > (I.e. we can't work this into existing Lisps like CL implementations without going down to that level.) So, if I understand correctly, it would be possible to add it to CL or Scheme via reader macros or reader extension respectively? Although I can't imagine the specific implementation right now, I think reader macros/extensions are executed during parsing, before the code is expanded, so it should work? With some caveats of course (some more tokens would be needed to identify where the `.` should be treated specially, I think). Either way, use of the dot/improper lists for applying variadic functions is great, I love the symmetry between definition and application here. Makes me wonder why isn't this handled like that in other Lisps? It seems so natural once you see it... Ah, regarding the slices, I, of course, have to ask - what is `dwim` and how is it implemented? If my guess is correct, `dwim` stands for "Do What I Mean", which would certainly fit in this case :D But it seems to work on everything collection-like. In Racket there's a sequence protocol you could use here (used to great effect in data/collection package[1]), in CL it would be a simple generic method with implementation for various kinds of collections, but how is it done in TXR Lisp? --- Anyway - if there's ever anyone other than us reading this thread ;) - the TLDR would be that Lisps can, and do, have notations above and beyond bare s-exps. These notations tend to have a deep impact on both convenience during writing and on readability later. There are differences between Lisps in terms of support for additional notations, but you can introduce them (aka. steal from other Lisps ;)) yourself if you find a particular extension to be useful in your situation. An example of this would be the "threading macros", `->` and `->>`, which are now available in every Lisp out there, even though they were first implemented in Clojure only (I think - was there "prior art" somewhere else, maybe?) Anyway, that's part of what makes Lisps so incredibly powerful! --- EDIT: reader macros. I understand your position, and most of the notations can be introduced as normal macros anyway, but... without them you're f+++ed if there's some notation you'd like to have, but which cannot be introduced without modifying the reader. Yes, compared to the number of possible syntax extensions the number of useful syntax extensions is frighteningly small. Yes, it's a major way of introducing incompatibilities. Clojure & Rich Hickey also excluded reader macros, adding to the previous argument one of security, ie. Clojure, when used as a data interchange format, should not have behave differently in different environments because of loaded reader extensions, plus it's rather scary that a simple `read` can format your disk, kill your cat, steal your car and so on. I agree that in most cases they should not be used... but. That "but" still remains in my head :) An example: there's a package for CL which allows you to alias module names. Writing them long-hand over and over again is less than ideal, to be honest. Due to how CL module system works it's impossible to achieve such aliasing with normal macros, you have to drop one level below. It certainly introduces an incompatibility, but it made writing code so much more pleasant! So anyway: TXR Lisp and more or less Clojure have a lot of notations built-in and maybe this is enough. But some other Lisps, without reader macros, would be stuck in the '80s as far as notational convenience go. So, to me, even with all the downsides (and in the context of said Lisps), reader macros are an important tool which allows for language evolution and improvement over the years. --- [1] https://docs.racket-lang.org/collections/collections-example... |
Can we have (func arg . rest) using just read macros in CL? It's not so simple because we cannot blindly hijack the parenthesis and turn every dot notation into apply. It has to be the expander recognizing it. The TXR Lisp expander knows that it's dealing with a function call, because the symbol in the CAR position isn't a macro or special operator. Only then does it check for an improper list, and do the "dot apply transform". So this has to be worked into the code expander. (Thus if we work portably over top of CL, we are writing our own code walker and not relying on the CL one.)
> But some other Lisps, without reader macros, would be stuck in the '80s as far as notational convenience go.
Right; that's because all the usual notational conveniences like quote, backquote and whatnot predate the 1980's by quite a bit, and there hasn't been anything new.
> An example: there's a package for CL which allows you to alias module names.
Are you talking about package local nicknames? That's an example of something being rolled into implementations. If everyone was on board in integrating the feature, programs wouldn't have to hack this.
Though it requires hooking into the reader, I don't believe that the feature changes syntax. If foo is a package local nickname, then foo:whatever is the same syntax.
So at least this will not break editing support.
Most uses of read macros break editor support. Syntax coloring and other features break.
Read macros lack print support. For instance, if the 'X syntax weren't built in, it couldn't be produced with read macros completely. The input notation can be produced with read macros; however, Lisps also print the (quote X) object as 'X.
Almost all the junk I put into TXR Lisp prints, e.g.:
Even the singleton consing dot, at least if in a lambda: > I love the symmetry between definition and application hereIt's just good old "declaration follows use":
:)> The syntax for "rest args" is similar in Schemes - `(define (f . args) ...)`
But I don't think they have (lambda (. args) ...). That's lateral thinking. Why not have a consing dot with nothing in front of it such that (. x) is just x?
Here is the intellectual precedent for it, right in ANSI Lisp:
I.e. the terminating atom of an inproper list is itself an "empty improper list", identical to that atom itself, just like an empty proper list is equivalent to the atom nil.Hence if () is the empty list, that being the terminator nil, then (. 3) is the empty improper list terminated by 3, which is just the terminator 3 itself.
> what is `dwim` and how is it implemented?
dwim is implemented half trivially, half not-so-trivially. The basic idea is that it is just a placeholder which displaces the operator to the second position of the form, where it is consequently evaluated as an expression, allowing for Lisp-1 style invocation without a funcall symbol. Well, of course dwim is that symbol, but it's hidden behind the [ ] notation.
The not-so-trivial part is that, dwim also applies a special treatment to those of its arguments which happen to be symbols. These are resolved in a single namespace that folds together variable and function bindings. This allows us to do things like [mapcar list '(1 2 3)], even though we are in what is fundamentally a Lisp-2. Why this is not-so-trivial is that it's deeply ingrained into the expander, evaluator and compiler, which understand the conflated namespace and implement it properly (in the face of lexical functions, macros, symbol macros and such).
All the semantics under dwim for objects being callable as functions is actually implemented in the function call mechanism, not in dwim.
By brief example:
Simply, the semantics of calling a sequence with a single argument which is a range object is to perform a subsequence extraction.Dwim differs from call in that it is an assignment place:
But this is not any sort of magic; it's played out with macros and some run-time-support: There is a special sys:dwim-set run-time function which contains the range assignment semantics. It is allowed to mutate the object, but that's not always possible; therefore, the return value must be captured. The place expander framework takes care of it, generating the sys:setq assignment back to the c variable, just in case a new object is required. (The thing that newbie Lisp programmers forget to do when using functions like remove-if or append!)