Update: Now part of brev-separate.
In Scheme, only #f
is falsy. Which is good, for a foundational,
building-block, minimalist language, but kinda cumbersome for the more
sloppy, magicy, implicit programming style I’m into. I kinda want a
higher “wat” factor and fewer type-related hurdles and hoops.
We can use fast-generic to make an empty?
predicate to see when we
are dealing with empty lists, empty strings, zero numbers etc.
(import fast-generic)
(define-type number number?)
(define-type string string?)
(define-type list list?)
(define-generic (empty? (any x)) (not x))
(define-generic (empty? (number x)) (zero? x))
(define-generic (empty? (string x)) (equal? "" x))
(define-generic (empty? (list x)) (null? x))
(map empty? (list #f #t 3 0 -4 "foo" "" '(a b c) '(a) '()))
⇒ (#t #f #f #t #f #f #t #f #f #t)
If it was only those three types, a custom dispatcher would’ve been better than using fast-generic, but, I want you to be able to extend it with all kinds of custom types. Vectors, hashes, types of your own creation…
In the compiled version, that string empty?
might be better as
(##core#inline "C_i_string_null_p" x)
to shave off some cycles.
I mean, using the overloaded, ducky, generic empty?
over the specific
preds is already not fast so in your innermosts of inner loops, just
use the real preds. But for everyday programming this is gonna be
sweet and comfortable.
For example, it allows us to do an eif
that treats empty?
things
as falsy. It’s also anaphoric because of course it is.
(define-ir-syntax* eif
((eif test yes no)
`(let ((,(inject 'it) ,test))
(if (empty? ,(inject 'it))
,no ,yes))))
(eif "" 'never 'always)
⇒ always
You can also make econd
using eif
. Implementation elided here, since it’s just a string replace on cond
.
(econd ("" 'never) (13 (+ it it)) (else 'always))
⇒ 26
It also allows us to do make a function that wraps the whole thing in
a guard to return '()
on an empty input.
(define-syntax-rule
(define-some (name arg args ...) body)
(define (name arg args ...)
(if (empty? arg)
'()
body)))
(define-some (aaas num)
(cons #\a (aaas (sub1 num))))
(list->string (aaas 5))
⇒ "aaaaa"