Preferences

Writing a reader macro that allows for something like...

  [some-numbers 0]
...to get the first (many programming languages make this mistake, using 0 to refer to the first element of a collection, so we can forgive CL for this) element. But I'm curious how you can write...

  (object -> slot)
...without getting an error about OBJECT not being a valid function or macro.

> so we can forgive CL for this

The 1962 dated Lisp 1.5 Programmer's Manual already describes a 0 based array feature. Lisp was clearly one of the historic instigators of zero based array, rather than just playing along.

Yes, but the various Lisps that Common Lisp is the more-or-less common subset of are (were?) all 0-indexed. Between easy heap implementation (left is (ash index 1), right is (1+ (ash index 1)), parent is (ash index -1)) and easy last element selection (nth seq (length seq)) I prefer 1-indexing, but I realize that's an unpopular opinion.
A late reply but it's worth addressing one way of doing this. First, your concern about object not being a valid function or macro isn't relevant at read time. Second, note that Lisp already has similar syntax: '(1 . 2) is essentially (cons 1 2). Implementing this type of syntax is not a privilege of the implementation alone. You're allowed to redefine your own reader for left paren. In SBCL:

    CL-USER> (get-macro-character #\()
    SB-IMPL::READ-LIST
You can write `(set-macro-character #\( 'sb-impl::read-list)` and everything continues to work just fine. You can also jump-to-source and modify it if you want -- though it's cleaner to just copy it out to your own project, that's what I did for a quick hack/proof of concept. Essentially I added before the existing (when...) which handles the special dot syntax:

      (when (and (eq firstchar #\-)
                 (eq (peek-char t stream t nil t) #\>))
        (read-char stream t) ; actually read the nextchar > to discard it
        (let ((next-obj (read stream)))
          (sb-impl::flush-whitespace stream rt)
          (return `(slot-value ,@listtail ',next-obj))))
I won't claim this is good or proper, but it shows that it's quite feasible. We've turned (foo -> bar) into (slot-value foo 'bar).

    CL-USER> (defclass vec2 ()
      ((x :initarg :x)
       (y :initarg :y)))
    #<STANDARD-CLASS COMMON-LISP-USER::VEC2>
    CL-USER> (defparameter vec (make-instance 'vec2 :x 3 :y 4))
    VEC
    CL-USER> (vec -> y)
    4
    CL-USER> (read-from-string "(print (vec -> x))")
    (PRINT (SLOT-VALUE VEC 'X))
    18
Personally I wouldn't use this even if it was more properly/carefully implemented. (There's really no reason to replace the default left-paren reader, and no reason we have to have a space surrounding the "->". One thing I like about the infix reader macro package https://github.com/quil-lang/cmu-infix is that it doesn't care about spaces, I can write #I(1+1 + 4) and get 6.) I'm quite happy putting my class in its own package, and thus getting the primary tab-completion behavior I care about. e.g. "(ma:<tab>" could complete to "(math:" and then "(math:v<tab>" could complete to a list of options like "vector-x" "vector-y" or so on. I also like the somewhat unusual approach of naming my accessors with a dot prefix, e.g. (.x vec) and (.y vec), or even (math:.x vec) if I haven't imported the symbol.
Good things are worth waiting for. I never considered making a reader macro for a regular opening bracket, that's equal parts genius and insanity.
And also make sure that slot is a symbol in the correct package. Or do like Elisp and do without packages but then have a 16 character prefix

This item has no comments currently.

Keyboard Shortcuts

Story Lists

j
Next story
k
Previous story
Shift+j
Last story
Shift+k
First story
o Enter
Go to story URL
c
Go to comments
u
Go to author

Navigation

Shift+t
Go to top stories
Shift+n
Go to new stories
Shift+b
Go to best stories
Shift+a
Go to Ask HN
Shift+s
Go to Show HN

Miscellaneous

?
Show this modal