When comparing various syntax options, I find it helpful to try out various
code fragments to see if they "make sense". So here are two other examples
I've worked out. I did this by hand, so there may be an error or two, but the
point is to look at it, to see if the results are comprehensible... and if the
syntax is useful.
One thing I learned from the Matrix multiply example - if {} FORCES an infix
interpretation, and you don't want to REQUIRE that the "group" operator be used
everywhere, you really need an unfixed [...] to mean "an s-expression, but keep
accepting sweet-expressions inside it". This is in addition to unprefixed {..}
to FORCE infix interpretation, and unprefixed (...) to force raw s-expression
interpretation.
So, here are two more examples:add-if-all-numbers and matrix multiply routine
mat-mat-mul. The first is typical "few infix operations" but lots of nested
function calls; the second has many infix operators, and also uses lists of
lists (e.g., in a do loop):
Question is - which ones are readable/understandable? Which ones are not?
* Here's a Scheme example from Wikipedia - specifically the article
"Scheme (programming language)". This example adds an arbitrary
list of numbers, and if a non-numeric value is found in
the list the procedure is aborted immediately and the constant value #f
(false) is returned. This is achieved by capturing the current continuation in
the variable exit and using it as an "escape procedure".
+ Original Scheme:
(define (add-if-all-numbers lst)
(call/cc
(lambda (exit)
(let loop ((lst lst) (sum 0))
(if (null? lst) sum
(if (not (number? (car lst))) (exit #f)
(+ (car lst) (loop (cdr lst)))))))))
+ Sweet-expressions, version 1:
define add-if-all-numbers(lst)
call/cc
lambda (exit)
let loop ((lst lst) (sum 0))
if null?(lst)
sum
if not(number?(car(lst)))
exit(#f)
(car(lst) + loop(cdr(lst)))
+ Infix default, {..} for grouping, [...] disables infix, f(...) function call:
define add-if-all-numbers(lst)
call/cc
lambda (exit)
let loop ((lst lst) (sum 0))
if null?(lst)
sum
if not(number?(car(lst)))
exit(#f)
car(lst) + loop(cdr(lst))
+ Non-Infix default, {..} around all infix:
define add-if-all-numbers(lst)
call/cc
lambda (exit)
let loop ((lst lst) (sum 0))
if null?(lst)
sum
if not(number?(car(lst)))
exit(#f)
{car(lst) + loop(cdr(lst))}
* Matrix multiply example from http://www.scheme.com/tspl2d/examples.html
mat-mat-mul multiplies one matrix by another, after verifying
that the first matrix has as many columns as the second matrix has rows.
+ Original Scheme:
(define mat-mat-mul
(lambda (m1 m2)
(let* ((nr1 (matrix-rows m1))
(nr2 (matrix-rows m2))
(nc2 (matrix-columns m2))
(r (make-matrix nr1 nc2)))
(if (not (= (matrix-columns m1) nr2))
(match-error m1 m2))
(do ((i 0 (+ i 1)))
((= i nr1) r)
(do ((j 0 (+ j 1)))
((= j nc2))
(do ((k 0 (+ k 1))
(a 0
(+ a
(* (matrix-ref m1 i k)
(matrix-ref m2 k j)))))
((= k nr2)
(matrix-set! r i j a))))))))
+ Sweet-expressions, version 1:
define mat-mat-mul
lambda (m1 m2)
let* ( (nr1 matrix-rows(m1))
(nr2 matrix-rows(m2))
(nc2 matrix-columns(m2))
(r make-matrix(nr1 nc2)))
if not(matrix-columns(m1) = nr2) ; f(infix) handled automatically.
match-error(m1 m2)
do ((i 0 (i + 1)))
((i = nr1) r)
do ((j 0 (j + 1)))
((j = nc2))
do ((k 0 (k + 1))
(a 0 (a + (matrix-ref(m1 i k) * matrix-ref(m2 k j)))))
((k = nr2) matrix-set!(r i j a))
Or, if you use groups:
define mat-mat-mul
lambda (m1 m2)
let*
group
nr1 matrix-rows(m1)
nr2 matrix-rows(m2)
nc2 matrix-columns(m2)
r make-matrix(nr1 nc2)
if not(matrix-columns(m1) = nr2) ; f(infix) handled automatically.
match-error(m1 m2)
do
group
i 0 (i + 1)
group
(i = nr1) r
do
group
j 0 (j + 1)
group
j = nc2
do
group
k 0 (k + 1)
a 0 (a + (matrix-ref(m1 i k) * matrix-ref(m2 k j)))
group
(k = nr2) matrix-set!(r i j a)
+ Infix default, {..} for grouping, [...] disables infix, f(...) function call,
(...) switches to regular s-expression reader:
define mat-mat-mul
lambda (m1 m2)
let* { {nr1 matrix-rows(m1)}
{nr2 matrix-rows(m2)}
{nc2 matrix-columns(m2)}
{r make-matrix(nr1 nc2)}}
if not(matrix-columns(m1) = nr2)
match-error(m1 m2)
do {{i 0 {i + 1}}}
{{i = nr1} r}
do {{j 0 {j + 1}}}
{{j = nc2}}
do {{k 0 {k + 1}}
{a 0 {a + {matrix-ref(m1 i k) * matrix-ref(m2 k j)}}}}
{{k = nr2} matrix-set!(r i j a)}
Or, if you use groups:
define mat-mat-mul
lambda (m1 m2)
let*
group
nr1 matrix-rows(m1)
nr2 matrix-rows(m2)
nc2 matrix-columns(m2)
r make-matrix(nr1 nc2)
if not(matrix-columns(m1) = nr2) ; f(infix) handled automatically.
match-error(m1 m2)
do
group
i 0 {i + 1}
group
{i = nr1} r
do
group
j 0 {j + 1}
group
j = nc2
do
group
k 0 {k + 1}
a 0 {a + {matrix-ref(m1 i k) * matrix-ref(m2 k j)}}
group
{k = nr2} matrix-set!(r i j a)
+ Non-Infix default, {..} around all infix, unprefixed [...] is like
(...) but doesn't disable sweet-expressions, f{...} is f({infix}),
(...) switches to regular s-expression reader:
define mat-mat-mul
lambda [m1 m2]
let* [ [nr1 matrix-rows(m1)]
[nr2 matrix-rows(m2)]
[nc2 matrix-columns(m2)]
[r make-matrix(nr1 nc2)]]
if not{matrix-columns(m1) = nr2} ; f{infix}
match-error(m1 m2)
do [[i 0 {i + 1}]]
[{i = nr1} r]
do [[j 0 {j + 1}]]
[{j = nc2}]
do [[k 0 {k + 1}]
[a 0 {a + {matrix-ref(m1 i k) * matrix-ref(m2 k j)}}]]
[{k = nr2} matrix-set!(r i j a)]
Or, if you use groups:
define mat-mat-mul
lambda [m1 m2]
let*
group
nr1 matrix-rows(m1)
nr2 matrix-rows(m2)
nc2 matrix-columns(m2)
r make-matrix(nr1 nc2)
if not{matrix-columns(m1) = nr2} ; f{infix} = f({infix}).
match-error(m1 m2)
do
group
i 0 {i + 1}
group
{i = nr1} r
do
group
j 0 {j + 1}
group
j = nc2
do
group
k 0 {k + 1}
a 0 {a + {matrix-ref(m1 i k) * matrix-ref(m2 k j)}}
group
{k = nr2} matrix-set!(r i j a)