Idiomdrottning’s homepage

The Empty Truth

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.

eif

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

define-some

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"