Idiomdrottning’s homepage

Sloppy tree accessors

I learned Scheme before Common Lisp and Emacs Lisp (well, my very first exposure was to the very basics of Emacs Lisp, I didn’t even get to defun part), but I’ve done my fair share of Common Lisp since and I’ve been vindicated in one thing I kinda independently invented and I’m sure a lot of other impatient, lazy, and hubristic hacks came up with the same thing.

I often found myself wanting to do things on car, if there is a car. Or on cdr, if there is a cdr. And so on.

I called these accessors scar and scdr, and I at first thought “s for ‘safe’ since they won’t error! I am so smart!” but soon enough realized these were anything but safe, since they fail silently!

I started using them a little bit less, and… veeery carefully. Guiltily and self-deprecatingly, I started thinking of the s as standing for Sandra’s. When I made brev, which has an arbitrary-depth generator for them (you can (make-sloppy-tree-accessor scdaaddaaddadddadaaaadr) or whatever), I came up with the name “sloppy” and started feeling less bad.

All of this stems from '() and #f being two separate values in Scheme where Common Lisp and Emacs Lisp only has nil. So on there, you can (cadar ()) and it doesn’t bork. Vindicated!

I feel that with sloppy tree accessors carefully and judiciously applied, and eif (which treats empty as falsy) as an option next to the normal, safe if, we can have the best of both worlds. Convenience when we know what we’re doing, but retaining the ability to discriminate between these value types when we need to.

Update

Elias wrote in, asking:

Being someone who definitely prefers CL, I wonder when you need to discriminate between them?

I love the convenience of CL in this particular regard.

Here’s my take: 90% of the time I don’t care. 8% I want the CL style. 2% of the time I do want them as separate.

One example is when you have a function that can return ``#f if it doesn’t find what it’s looking for, or a list (including the empty list) if it does. Only way to do this on CL is with boxing (sorta like the Haskell “Just Maybe” ridiculousness).

alist-ref from Chicken’s base module is an obvious example.

(aif (alist-ref foo bar)
     (frobnicate it)
     (not found so update the alist instead))

To get the CL behavior, that’s why I made eif which is an aif that does treat empty as falsy.

As an aside, those “90% of the time when I don’t care”, it does find me some bugs, so. 🤷🏻‍♀️ (That’s also what all those straight-jacket typed languages fans keep writing in about when they hear that I love dynamic typing and hate the redundant human compiler at work annotating types and duplicating code. Even though brev has dependent types (albeit runtime, not compile time).)

But in CL’s defense, half of those “bugs” wouldn’t even be bugs in CL. If I’m like

(if (eq foo (car bar))
    this
    that)

then that’s just as good (on CL) and non-buggy (on CL) as the annoying, overly explicit Scheme style of

(if (and (pair? bar)
         (eq? foo (car bar)))
    this
    that)

That’s what sloppy tree accessors fix:

(if (eq? foo (scar bar))
    this
    that)

Brev. Making Scheme almost as good as CL since 2021 🤦🏻‍♀️