summaryrefslogtreecommitdiff
path: root/src/content/tils/2021/04/24/scm-nif.adoc
blob: d057d665cd43617e9d9180635c60a310df868552 (about) (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
= Three-way conditional for number signs on Lisp
:categories: lisp, scheme, common lisp
:sort: 2
:updatedat: 2021-08-14

:on-lisp: https://www.paulgraham.com/onlisptext.html
:sicp: https://mitpress.mit.edu/sites/default/files/sicp/index.html

A useful macro from Paul Graham's {on-lisp}[On Lisp] book:

[source,lisp]
----
(defmacro nif (expr pos zero neg)
  (let ((g (gensym)))
    `(let ((,g ,expr))
       (cond ((plusp ,g) ,pos)
             ((zerop ,g) ,zero)
             (t ,neg)))))
----

After I looked at this macro, I started seeing opportunities to using it in many
places, and yet I didn't see anyone else using it.

The latest example I can think of is section 1.3.3 of {sicp}[Structure and
Interpretation of Computer Programs], which I was reading recently:

[source,scheme]
----
(define (search f neg-point pos-point)
  (let ((midpoint (average neg-point pos-point)))
    (if (close-enough? neg-point post-point)
        midpoint
        (let ((test-value (f midpoint)))
          (cond ((positive? test-value)
                 (search f neg-point midpoint))
                ((negative? test-value)
                 (search f midpoint pos-point))
                (else midpoint))))))
----

Not that the book should introduce such macro this early, but I couldn't avoid
feeling bothered by not using the `nif` macro, which could even remove the need
for the intermediate `test-value` variable:

[source,scheme]
----
(define (search f neg-point pos-point)
  (let ((midpoint (average neg-point pos-point)))
    (if (close-enough? neg-point post-point)
        midpoint
        (nif (f midpoint)
             (search f neg-point midpoint)
             (midpoint)
             (search f midpoint pos-point)))))
----

It also avoids `cond`'s extra clunky parentheses for grouping, which is
unnecessary but built-in.

As a macro, I personally feel it tilts the balance towards expressivenes despite
its extra cognitive load toll.