To get the flavor of the language, let us first consider a few simple Pascal subroutines and their equivalents in Dylan. Here we have a Pascal function for calculating the distance between two points in a two dimensional plane, using the Pythagorean theorem:
function distance(x1: real; y1: real; x2: real; y2: real): real;
begin
distance := sqrt((x2-x1) * (x2-x1) + (y2-y1) * (y2-y1))
end;
and here's an equivalent function in Dylan:
define method distance (x1 :: <real>, y1 :: <real>, x2 :: <real>, y2 :: <real>)
=> distance :: <real>;
sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
end method distance;
Let's first look at the syntactic differences between the two
functions. First, Pascal introduces the definition with the keyword
function where Dylan uses the phrase define
method. Define is used in Dylan when defining all
module variables, which are the equivalent of program
variables, types, functions, and procedures in Pascal. We'll see
later why Dylan calls this function a method; for now, it is
useful to think of methods in Dylan as the same as functions in
Pascal.
The next thing to notice is that Dylan uses the double colon symbol
(::) to separate variables from their types; we'll see
later that a single colon is used for another purpose, which is why
the double colon is used in type declarations. Also note that the
Dylan program has a space between the variable name and the double
colon.
The next important difference between the two examples is that the
name of the type, real, is enclosed in "angle brackets"
(the less-than and greater-than signs) in the Dylan version. The
reason for this is that, unlike in Pascal, types and variables live in
the same name space in Dylan. For example, in Pascal, one can
have a type named list and a variable named
list at the same time, and the meaning of the identifier
list refers to one or the other depending on context. In
Dylan, there isn't always enough context to know whether something is
a type or value -- and, in fact, types are values in Dylan --
so a convention is used to separate types from non-types in order to
avoid confusion and name clashes. That convention is putting angle
brackets around the names of types. (C programmers face a similar
problem, and at least two distinct conventions have evolved: using
identifiers with the first letter capitalized or with a suffix of
_t for type names.)
The convention for type names brings up another difference between Dylan and Pascal: What characters are legal in names? Pascal, C, and many traditional languages restrict the set of characters allowed in names to the letters, digits, and perhaps a few graphical characters, such as the underscore or dollar sign. Dylan is more flexible: all those characters are legal, but so are many graphical characters, such as hyphens, asterisks, question marks, exclamation marks, and, as we have seen, the greater-than and less-than signs. The general rule is that a Dylan identifier can't start with a digit and should contain at least one letter, but any characters that don't otherwise have special meaning are allowed.
This flexibility does come at some cost: because the character set for
variable names overlaps with the character set for operators, we need
to put spaces between variable names and operators. For example,
x2 - x1 is two two-character long variables separated by
the minus operator and means the same thing as it does in Pascal, but
x2-x1 is a single five-character long variable name in
Dylan. This may seem confusing or awkward at first to programmers not
used to putting space between variables and operators, but it adds
flexibility in the choice of names.
Now back to the example function. In Pascal, the return type of the
function is declared with a colon, just like the type of a variable,
where Dylan uses an arrow (=>) combined with what looks
like a variable declaration. The return description for our function
says that the function returns the distance, and that the result has
the type <real>. The name used in a return
description is for documentation purposes only. Upon reflection, one
might come to the conclusion that the name is unnecessary as
documentation, since the function name should describe the meaning of
the return value. Later, we'll see a reason why Dylan puts the name
in the result.
Let's skip the contents of the function body of the function for a
moment, and notice that Pascal surrounds the body with the words
begin and end. Dylan is similar, except
that the word begin is omitted. All methods must have
bodies, so where it begins is implicit: right after the declaration of
parameters and return values. The word method and the
name of the method are repeated after the word end; both
method and the name are optional after the
end, but I personally prefer to put them in, so I've done
that here. Individual styles vary, and people who think that the
words after end clutter the program are free to leave
them out.
Now, let's look at the bodies of the functions. Aside from the spaces
separating the operators from the variable names in the Dylan version,
there is one major difference. In Pascal, the pseudo-variable with
the same name as the function, distance, is assigned the
value to return from the function. In Dylan, on the other hand, the
body is treated as an expression, and the value of that expression is
returned by the function. If that body contains multiple statements
or expressions separated by semicolons, the function returns whatever
the last expression returns. (C is different from both Dylan and
Pascal in this regard: it uses the return statement to
both return the value and exit immediately from the function.)
The last important detail to notice is that, aside from the issue of spaces, the expression used to calculate the distance is the same in both Dylan and Pascal.
define method distance (x1, y1, x2, y2)
sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1))
end method distance;
When used with four real numbers, this distance function produces the
same result as the original. But this version will work just as well
on integers. (In fact, the original version also works on integers,
because the type <integer> is a subtype of
<real>; that is, all integers are also reals, which
matches our mathematical intuition. The type
<float> in Dylan corresponds more directly with
Pascal's real type.)
Since any value can be used as an argument to distance, what happens
if we use something which doesn't make sense, like distance(0,
0, 3, "four")? The function starts as normal, but when it
tries to subtract 0 from "four", the program
stops with an error, since it makes no sense to substract a number
from a string. If we had used the original version of the distance
function, we would have gotten the error when we tried to call
distance.
For now, we'll omit most type declarations in our Dylan examples. Later, we'll see some of the reasons for using them other than catching errors.
x2 - x1 is done twice in order to multiple the result by itself; the same is then done for the y values. To reduce the work that the function does, we can store the results of the subtractions in local variables. (Many compilers will do this kind of optimization -- known as common subexpression elimination -- without requiring the code to be rewritten.) In Pascal, that might look like:
function distance(x1: real; y1: real; x2: real; y2: real): real;
var
deltaX, deltaY: real;
begin
deltaX := x2 - x1;
deltaY := y2 - y1;
distance := sqrt(deltaX * deltaX + deltaY * deltaY)
end;
which could be written in Dylan as
define method distance (x1, y1, x2, y2)
let delta-x = x2 - x1;
let delta-y = y2 - y1;
sqrt(delta-x * delta-x + delta-y * delta-y)
end method distance;
First, we see that where Pascal puts the definition of the local variables in a separate section of the function definition, Dylan puts them in the body. Next, we see that the definition of the local variables includes the initialization. In general, when you define a variable in Dylan, you give it a value at the same time -- in this way, you do not have to worry about unitialized variables.
Also notice that where, in Pascal, mixedCaseNames are
often used to separate words in long identifiers, Dylan conventionally
uses hyphen-separated-names.
function distance(x1: real; y1: real; x2: real; y2: real): real;
function square(n: real): real;
begin
square := n * n
end;
begin
distance := sqrt(square(x2 - x1) + square(y2 - y1))
end;
which could be written in Dylan as
define method distance (x1, y1, x2, y2)
local method square (n :: <real>)
n * n
end method square;
sqrt(square(x2 - x1) + square(y2 - y1))
end method distance;
Like local variables created with let, local methods can
appear anywhere inside a body.
Copyright © 1995 Paul Haahr. All rights reserved.
Paul Haahr