by José Bollo
This SRFI is currently in draft status. Here is an explanation of each status that a SRFI can hold. To provide input on this SRFI, please send email to srfi-266@nospamsrfi.schemers.org. To subscribe to the list, follow these instructions. You can access previous messages via the mailing list archive.
The syntax expr allows one to
write arithmetic expressions using a syntax near to mathematical
notation, potentially improving the readability of Scheme programs.
syntax-rules macros is not easy, and error reports may be difficult to express.Scheme programmers expressing arithmetic expression see all over their code the gap between what they learnt at school for expressing arithmetic formulae and what is written in Scheme.
Here is an example of Scheme for computing the root of a quadratic.
(let-values (((q r) (floor/ count size)))
(if (zero? r) q (+ q 1)))
(let ((rdelta (sqrt (- (square b) (* 4 a c)))))
(values (/ (+ (- b) rdelta) (* 2 a))
(/ (- (- b) rdelta) (* 2 a))))
; computes the smallest integer k >= 0 such that (45-5k)% <= dark/total <= (55+5k)%)
(define k (- (quotient (+ (abs (- (* dark 20) (* total 10))) total -1) total) 1))
This SRFI describes the syntax expr that fills the gap between
Scheme and mathematics, at least for arithmetic. Using this syntax, the above
examples can be written as below:
(expr floor/ (count size) as q r in q + ? positive? r)
(expr sqrt(square b - 4 * a * c) as delta in
values ((- b + delta) / (2 * a)
(- b - delta) / (2 * a)))
(define k (expr (((abs dark) * 20 - total * 10) + total - 1) \ total - 1))
The syntax expr is inspired by the eponymous UNIX tool expr.
The expression at its right is expressed using almost standard formulae.
Writing arithmetic expressions is common when programming.
Writing logic expressions and comparison expressions is also
quite common. For this reason, the syntax expr comes with
flavours allowing these expressions, too. Thus it is possible to write:
(if (expr a < 0 and (k or b + c <> 9)))
...
instead of:
(if (and (< a 0) (or k (not (= (+ b c) 9))))
...
SRFI 105, Curly-infix-expressions, already introduced the ability to write expressions in a more usual way. The current SRFI differs for the the following reasons:
However, SRFI-105 is compatible with this SRFI when $nfx$ and is defined as below:
(define-syntax $nfx$
(syntax-rules ()
((_ x ...) (expr x ...))))
This SRFI exports only one syntax item: expr.
According to SRFI 261, this syntactic item can be imported
using:
(import (srfi srfi-266))
or on system supporting it, using:
(import (srfi expr-266))
The syntactic item expr is followed by an expression
whose elements E are separated by space
(except if enclosed in braces):
(expr E ...)
The expression E ... is made of variables,
numbers and operators.
Operators are keywords, and can't be redefined. They are scanned as symbols, not as bindings to values. This rule is required for providing an unambiguous translation of a rich set of operators, and to provision it without requiring any import of any library.
Here is the list of operators defined for expr:
| operator | type | priority | operation |
|---|---|---|---|
| @ | left | 10 | vector-ref |
| @. | left | 10 | list-ref |
| @@ | left | 10 | bytevector-u8-ref |
| @ | prefix | 10 | unbox |
| ** | left | 20 | expt |
| - | prefix | 30 | - |
| + | prefix | 30 | + |
| not | prefix | 30 | not |
| ? | prefix | 30 | boolean --> 0 or 1 |
| * | list | 40 | * |
| / | list | 40 | / |
| \ | left | 40 | quotient |
| % | left | 40 | remainder |
| func | 50 | function call | |
| + | list | 50 | + |
| - | list | 50 | - |
| < | comp | 80 | < |
| > | comp | 80 | > |
| <= | comp | 80 | <= |
| >= | comp | 80 | >= |
| = | comp | 80 | = |
| != | left | 90 | not = |
| and | list | 130 | and |
| or | list | 140 | or |
| implies | left | 150 | implies |
| if else | ternary | 160 | if |
| as in | as | 160 | let-values |
| ~ | prefix | 30 | biwise-not |
| & | list | 100 | bitwise-and |
| ^ | list | 110 | bitwise-xor |
| : | list | 120 | bitwise-ior |
| ~& | left | 100 | bitwise-nand |
| ~^ | left | 110 | bitwise-eqv |
| ~: | left | 120 | bitwise-nor |
| fx- | prefix | 30 | fxneg |
| fx~ | prefix | 30 | fxnot |
| fx* | left | 40 | fx* |
| fx\ | left | 40 | fxquotient |
| fx% | left | 40 | fxremainder |
| fx+ | left | 50 | fx+ |
| fx- | left | 50 | fx- |
| fx<< | left | 60 | fxarithmetic-shift-left |
| fx>> | left | 60 | fxarithmetic-shift-right |
| fx< | comp | 80 | fx<? |
| fx> | comp | 80 | fx>? |
| fx<= | comp | 80 | fx<=? |
| fx>= | comp | 80 | fx>=? |
| fx= | comp | 80 | fx=? |
| fx!= | left | 90 | not fx=? |
| fx& | list | 100 | fxand |
| fx^ | list | 110 | fxxor |
| fx: | list | 120 | fxior |
| fx- | prefix | 30 | fl- |
| fl* | left | 40 | fl* |
| fl/ | left | 40 | fl/ |
| fl\ | left | 40 | flquotient |
| fl% | left | 40 | flremainder |
| fl+ | left | 50 | fl+ |
| fl- | left | 50 | fl- |
| fl< | comp | 80 | fl<? |
| fl> | comp | 80 | fl>? |
| fl<= | comp | 80 | fl<=? |
| fl>= | comp | 80 | fl>=? |
| fl= | comp | 80 | fl=? |
| fl!= | left | 90 | not fl=? |
Types of operators are: prefix, left, list, comp, func, ternary.
(OPERATION X).(OPERATION X Y).(OPERATION X Y Z...).X OPERATOR Y [OPERATOR Z]... becomes
(OPERATION X Y Z...).
X COMP1 [Y COMP1]... Z COMP2 A [COMP2 B]... becomes
(let ((tmp Z)) (and (COMP1 X Y... tmp) (COMP2 tmp A B...))).
X Y becomes
(X Y).
X Y Z becomes
(X (Y Z)) (right associativity).
X if Y else Z becomes
(if Y X Z).
X as Y ... in Z becomes
(let-values (((Y ...) X)) Z).
Expressions can also have sub-expression in parenthesis. In that case few cases are distinguished:
When argument of a call, spaces are interpreted as separation of arguments.
Then f (a b ...) expands as
((expr f) (expr a) (expr b) ...).
When at the place of a term, the sub expression (a ...)
expands as if replaced by (expr a ...).
When a subexpression only contains one subexpression, it is replaced
literaly by this subexpression. Then ((a ...))
becomes (a ...) without applying implicitely expr
to the enclosed subexpression.
Application of the syntax expr should transform the expression
to an equivalent scheme expression. It means that using expr has
no runtime cost. It also means that expr is not required to
optimize expressions. It is only required to translate the expression
(for example, (expr + - 3) should become (+ (- 3))
even if it looks like being -3).
For example:
(expr a + b + c < d <= x - y - z)
Should be translated to:
(let ((tmp d)) (and (< (+ a b c) tmp) (<= tmp (- x y z))))
Implementation of syntactic item expr can be made in many ways:
using standard Scheme macros, using implementation specific macros, or,
using internals of implementation.
The sample implementation given for guile 3 use syntax-case.
Source for the sample implementation.Many thanks to writers of the UNIX tool expr.
© 2026 José Bollo.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.