2 ms·
In TXR Lisp, I abstracted car/cdr to work with non-conses. $ txr This is the TXR Lisp interactive listener of TXR 300. Quit with :quit or Ctrl-D on an em
by kazinator 1y ago
In TXR Lisp, I abstracted car/cdr to work with non-conses.
$ txr
This is the TXR Lisp interactive listener of TXR 300.
Quit with :quit or Ctrl-D on an empty line. Ctrl-X ? for cheatsheet.
TXR is written, directed and produced by, not to mention starring, Kaz.
1> [mapcar chr-toupper "abc"]
"ABC"
There is an important function and concept called nullify. This deals with the fact that empty nonlists are not nil. nullify returns its argument (like identity) if the argument is a non-empty thing. If the argument is an empty sequence, it translates it to nil:
2> (nullify "")
nil
Thus:
3> (cdr "a")
nil
4> (car "abc")
#\a
5> (car "a")
#\a
6> (cdr "a")
nil
Here, nullify at work. (cdr "a") wants to be "", but gets nullified.
7> (car "ab")
#\a
8> (cadr "ab")
#\b
9> (cddr "ab")
nil
You can define a structure with car and cdr methods. Heck, rplaca and rplacd too!
10> (defstruct kons ()
bling blong
(:method car (me) me.bling)
(:method cdr (me) me.blong)
(:method rplaca (me splat) (set me.bling splat))
(:method rplacd (me plonk) (set me.blong plonk)))
#<struct-type kons>
11> (new kons bling 1 blong 2)
#S(kons bling 1 blong 2)
12> (car *11)
1
13> (cdr *11)
2
14> (rplaca *11 42)
#S(kons bling 42 blong 2)
You can have a nullify method which informs the language how to nullify your object. Our above kons structure needs nullify if it is to be a complete emulation of conses that work as sequences. https://www.nongnu.org/txr/txr-manpage.html#N-F001A7B8 https://www.nongnu.org/txr/txr-manpage.html#N-F001A7B8
This is not the only way to make an object which serves as a sequence; you can instead (or in addition!) implement array-like indexing via the lambda and lambda-set methods to have it vector-like rather than list-like.
The empty function and nullify are almost inverses; anyway they are implemented in the same spot of a 16000+ line file. The difference is that empty errors out on objects that are not iterable, whereas nullify takes objects of all types:
val nullify(val obj)
{
val self = lit("nullify");
seq_info_t si = seq_info(obj);
if (seq_iterable(si))
{
seq_iter_t iter;
val elem;
seq_iter_init_with_info(self, &iter, si);
return if2(seq_peek(&iter, &elem), obj);
}
return si.obj;
}
val empty(val seq)
{
val self = lit("empty");
val elem;
seq_iter_t iter;
seq_iter_init(self, &iter, seq);
return tnil(!seq_peek(&iter, &elem));
}
But, anyway, conses are good. TXR doubles down on conses.