3 ms·
I mean you can write a macro that let's you write (object -> slot) and transforms it to (slot object) "->" should be unused
by cenamus 2y ago
I mean you can write a macro that let's you write
(object -> slot)
and transforms it to
(slot object)
"->" should be unused
- tmtvl 2y agoWriting 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.
- fovc 2y agoAnd 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
- Jach 2y agoA 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 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.
- tmtvl 2y agoGood things are worth waiting for. I never considered making a reader macro for a regular opening bracket, that's equal parts genius and insanity.
- kazinator 2y ago> 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.
- tmtvl 2y agoYes, 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.