Operators in Dylan
Learn about arithmetic, comparison, logical, and assignment operators in Dylan with practical Docker-ready examples
Operators are the building blocks of expressions in any programming language. In Dylan, operators carry a special significance because of the language’s heritage: they are actually generic functions in disguise. The infix syntax you see — a + b, x < y, n * 2 — is syntactic sugar that the compiler translates into ordinary function calls like \+(a, b) and \<(x, y).
This dual nature is one of Dylan’s most elegant design decisions. It inherits the conceptual uniformity of Lisp (where everything is a function call) while wearing the friendly infix clothing of ALGOL-family languages. Because operators are generic functions, they participate in Dylan’s multiple-dispatch system, meaning + can be specialized for new types you define.
In this tutorial we will explore Dylan’s arithmetic, comparison, logical, and assignment operators, look at how operator precedence works, and see how Dylan’s dynamic typing interacts with arithmetic on mixed numeric types.
Arithmetic Operators
Dylan provides the familiar set of arithmetic operators: +, -, *, /, and unary -. These work across Dylan’s numeric tower (<integer>, <float>, <double-float>, etc.) and automatically perform contagion — when an integer meets a float, the result is a float.
Create a file named arithmetic.dylan:
| |
A few Dylan-specific points worth highlighting:
- Integer division with
/truncates toward zero (so17 / 5is3). For explicit truncating or flooring division, use the named functionstruncate/andfloor/. - Dylan does not have a
%operator for remainder. Instead it provides the generic functionsmoduloandremainder. - Numeric contagion promotes the result type: adding
<integer>and<float>produces a<float>.
Comparison and Logical Operators
Comparison operators in Dylan return #t (true) or #f (false). Equality has two flavours: = for value equality (which Dylan calls similarity) and == for identity (the same object in memory).
Logical combinators are written & for AND and | for OR, and they short-circuit. Negation is the function ~ (often pronounced “not”). One quirk worth knowing up front: in Dylan, only #f is false — every other value, including 0, the empty string "", and the empty list #(), is truthy.
Create a file named comparison.dylan:
| |
Notice the trailing ? on identifier names like in-range? and extreme?. Dylan, in the Lisp tradition, allows ? (and !, -, *) inside identifier names. It is a strong convention to suffix predicates with ?.
Assignment and Mutation
Dylan distinguishes carefully between binding (introducing a name with let or define) and assignment (changing what an existing mutable binding refers to). Assignment uses the := operator, not = — because = is reserved for the equality predicate.
Create a file named assignment.dylan:
| |
The last block illustrates an important point: v[0] := 7 is not a special form built into the compiler. It is shorthand for the generic function call element-setter(7, v, 0). Any container that defines an element-setter method automatically participates in this syntax. This is the same uniformity that lets you redefine + for your own classes.
Operator Precedence and Function-Style Calls
Dylan has a deliberately flat precedence model compared with C or Java. Arithmetic operators bind tighter than comparison operators, which bind tighter than the logical combinators & and | — but within arithmetic, * and / have the same precedence as + and - and are evaluated strictly left to right. This catches programmers from C-family languages by surprise, so when in doubt: parenthesize.
Because every operator is also a function, you can call them in prefix form by escaping the name with a backslash.
Create a file named precedence.dylan:
| |
The last example is the punchline of Dylan’s operator design: because + is just a function, you can pass it to reduce like any other value. Functional patterns fall out naturally.
Running with Docker
| |
Expected Output
Running arithmetic.dylan:
a + b = 22
a - b = 12
a * b = 85
a / b = 3
modulo(a, b) = 2
truncate/(a, b) = 3
a + 0.5 = 17.5
-a = -17
Running comparison.dylan:
x = y? #f
x < y? #t
x <= y? #t
x > y? #f
x ~= y? #t
s1 = s2 (similar)? #t
s1 == s2 (identical)? #f
0 < x < 100? #t
extreme x? #f
not extreme: #t
0 truthy? yes
"" truthy? yes
#f truthy? no
Running assignment.dylan:
initial counter = 0
after updates = 20
total = 75
vector contents: 7 8 9
Running precedence.dylan:
2 + 3 * 4 = 20 (Dylan: left-to-right)
2 + (3 * 4) = 14
(2 + 3) * 4 = 20
\+(10, 20) = 30
\<(5, 9) = #t
sum of #(1,2,3,4,5) via reduce(\+, ...) = 15
Key Concepts
- Operators are generic functions. Every infix operator in Dylan desugars to a function call, so operators participate in multiple dispatch and can be specialized for user-defined classes.
=is equality,:=is assignment. This separation removes a whole class of C-style bugs and frees=to be a useful predicate.- Two equalities:
=and==.=tests similarity (value equality) and is the one you usually want;==tests identity (same object). - Only
#fis false. Zero, the empty string, and the empty list are all truthy. Predicates always return#tor#fexplicitly. - No
%remainder operator. Use the generic functionsmoduloandremainder— they differ in how they handle negative numbers. - Precedence is flat within arithmetic.
*and/do not bind tighter than+and-; evaluation is strict left-to-right. Parenthesize when mixing. - Logical
&and|short-circuit and return the actual value that decided the expression — not just a boolean — so they double as Dylan’s “or-else” and “and-then” combinators. - Operators are values. You can pass
\+,\*,\<to higher-order functions likereduce,map, andchoose, which is where Dylan’s functional heritage shines through.
Running Today
All examples can be run using Docker:
docker pull codearchaeology/dylan:latest
Comments
Loading comments...
Leave a Comment