Hacker News new | ask | show | jobs
by kazinator 2580 days ago
Regarding 1+, add1 and so on, I called these succ and pred in TXR Lisp. This is inspired by a an operator keyword in Pascal. There are also ssucc sssucc and sssucc as well as ppred, ppred and pppred. That is inspired by our caddr friends, and also by Douglas Hofstadter's ..SSS0. Now (+ 1 x) is one character shorter than (succ x), and likely clearer to more people. But, succ can be used as a higher order function without requiring partial application of the 1. Where we would have (op + 1) we can just use succ.

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.:

  1> '(qref a b (c) d)
  a.b.(c).d
  2> '(rcons a b)
  a..b
Even the singleton consing dot, at least if in a lambda:

  3> '(lambda x y)
  (lambda (. x)
    y)
  4> '(dwim a b c)
  [a b c]
> I love the symmetry between definition and application here

It's just good old "declaration follows use":

   void (*func)(int);

   (*func)(42);
:)

> 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:

  $ clisp -q
  [1]> (append '(1) 2)
  (1 . 2)
  [2]> (append 2)
  2
  [3]> (list* 1 2)
  (1 . 2)
  [4]> (list* 2)
  2
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:

  1> ["abcd" 2..3]
  "c"
  2> (call "abcd" 2..3) ;; no "dwim" here at all
  "c"
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:

  1> (let ((c (copy "abcd")))
        (set [c 1..3] "X")
        c)
  "aXd"
But this is not any sort of magic; it's played out with macros and some run-time-support:

  2> (expand '(let ((c (copy "abcd")))
        (set [c 1..3] "X")
        c))
  (let ((c (copy "abcd")))
    (let ((#:g0073 c)
          (#:g0075 #R(1 3)))
      (sys:setq c (sys:dwim-set t #:g0073 #:g0075
                                "X"))
      "X")
    c)
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!)