|
|
Home
>
1. overview
>
1.3 How to help newcomers to the language get the right "mental models?
|
Previous
Next
|
|
|
|
|
|
|
|
|
Scott
E. Fahlman
|
A programming language can be thought of as
- a kind of universal glue for sticking
things together,
- plus a set of basic built-in data
types
(and, in some cases, a way to extend this set),
- plus some built-in operations for
working on those datatypes.
In some languages you can't see this structure because everything
is hopelessly muddled together, but it's there at some level. The
more general and flexible you make the basic glue and data types,
the fewer awful kludges you have to add in later.
In Dylan
In Dylan the object-oriented function call is the basic piece of
semantic glue, supported by
- the class inheritance machinery,
- the module system,
- the condition system, and
- the basic built-in control structures.
Dylan's function call has a few complex things built in from the start
- multiple inheritance,
- multiple arg dispatch,
- keyword args,
- multiple-value returns
but that's because experience has shown that these are very useful
for writing clear, efficient code. It's better to put them in now than
have to graft them on in some ugly and poorly integrated way later.
Even with all these things, you have a single central mechanism that
is reasonably simple and elegant -- a very small part of the total
language.
On top of that, you've got the built-in datatypes. In an
object-
oriented language, you only need a few well thought-out fundamental
types: vectors, characters, a few types of numbers, and so on.
Everything else can be added in a clean simple way using the object
system. It's relatively easy to break chunks of the language off into
libraries, because the library can define both new classes and new
methods to work on them. You don't have to decide in advance
whether the + operator should be overloaded to support vector
arithmetic, for example, because a library can define both the
necessary classes and the methods that extend the arithmetic
operations to cover these.
[...]
Dylan's syntax is more complex than Common Lisp's, but that's
because Dylan's designers thought it would make Dylan easier to
learn and more familiar to programmers who have worked in other
programming languages or who are already comfortable with infix
notation for arithmetic.
Dylan's macro system will be more complex than Common Lisp's,
but the goal is to make it easier and safer for average programmers
to do simple things. Lisp macros are very elegant and powerful, but
often require very deep thinking on the part of the programmer to
keep all the levels straight and make everything work reliably.
|
|
|
|
Scott Ribe
|
I'm having difficulty finding my around the Dylan libraries because
the methods are not contained in the classes! But I don't think
this is an inherent flaw in the way Dylan libraries are structured. I
think it's just a temporary thing while I get used to a paradigm
that is different than the one that I'm used to.
I've used different OO languages and libraries, but for more than
a decade (closer to 2) I've always thought of methods as being
part of classes. My problem with SI libraries is not a problem
with expectations or perception; it is a problem with the overly-
complex structure of those libraries.
|
|
Rob Myers
|
Classes as a concept don't have to contain methods, and indeed
even in C++ you can view a virtual method as an entity that
stretches down through the class hierachy. Syntactically in C++,
you implement (non-inline) member functions outside the class
curlies, and then you have friend functions...
I *love* Dylan's Generic Function system. Current OO thinking
seems to regard Objects as consisting of funtionality, not data.
Priviledging methods/functions equally compared to data objects
makes this world-view easier to express (strangely) as functions
don't hang off data so much, and I feel it gives a more balanced
view of the world.
Java's broken, broken, broken "OK, you've inherited ten ABCs
now code the funtionality" interface system is soooo weak (I
program Java for a living). MI ( multi inheritance) can save time
and effort, make elegant designs or be mis-used like anything
else. :-)
|
|
|
|
|
|
|
Raffel
Cavallaro
|
the Holy Grail of Dylan was to create a language, runtime, and
compiler that incorporated the best aspects of Common Lisp/Scheme
(dynamism - interacting with and/or modifying a running application,
generic functions, closures, macros) and go beyond them (OO from
the ground up - e.g., writing methods that specialize on individual
instances of built-in classes - not possible in CLOS, macros that are
hygenic - unlike Common Lisp macros), all while generating compiled
code that is as fast as the fastest statically compiled languages like
C, or Fortran.
|
|
Difficulty
|
There is a fundamental tension between abstraction and performance.
The task of language implementation is to “compile away the
abstraction”. We aim to drastically increase the abstraction facilities
available to the programmer, and we thereby set ourselves a difficult
implementation task.
|
|
Impact:
|
Abstraction, reflection, and dynamism are powerful tools, and by
putting them in the hands of programmers, we hope to increase the
adaptability and dynamism of future software.
|
|
Robert
Futrelle
|
All our experience with Lisp tells us that it is a fluid language that allows
rapid prototyping. The Dylan designers are trying to preserve that as well
as producing tight and fast object code.
[Therefore] Dylan goes a step beyond the languages of the past in
allowing explicit control over what should be left open and what should be
sealed, so that run-time efficiency can be gradually added in as the
system moves from development mode to production mode and back to
maintenance mode.
To build complex systems requires powerful tools. The power is
distributed between the language, the designer/programmer and the
development and run-time systems. It is not possible for truly powerful
tools to be extremely simple. Difficult problems require ingenious and non-
trivial solutions. Such is the complexity of the world.
It is only after years of experience of many people that we can see our
way clear to provide this new mix of flexibility and efficiency. That is
where new languages come from. Looking at the history of programming
languages, or for that matter, of the tools of any technical discipline,
there is only one thing that we can be sure of – new languages,
techniques, etc., will come along with total inevitability. It is only a matter
of what they turn out to be in each era and how long they survive until
something even more useful appears.
Bob Futrelle
*********************************************************
(I wear sandals a lot because I was so impressed by John McCarthy's
penchant for them when he was a faculty member and I was an
undergraduate back at MIT in the late 1950s -- such are the things that
stick in ones mind. Sandals are healthy for the feet, too. At that time, the
machine I used for my work had the car and cdr operations in hardware --
it was an IBM 704 mainframe, the same one that McCarthy was using.)
|
|
Martin
Rodgers
|
> I wear sandals a lot because I was so impressed by
John McCarthy's
> penchant for them when he was a faculty member and I was an
> undergraduate back at MIT in the late 1950s -- such are the things
> that stick in ones mind. Sandals are healthy for the feet, too.
I'm also wearing sandals, and yet I didn't know about JM's use of them.
Could it be a side-effect of using Lisp? I wonder. :-)
|
|
|
|
|
A design principle for Dylan was that readability was more
important than writability. We had a mantra that code is
read 10 times more often than it's written. I still believe in
that principle.
|
|
|
|
|
The Dylan language illustrates the tension between the desire to
maximize expressiveness and the need for concrete type
information to address pragmatic concerns such as execution
efficiency and application size when delivering applications.
The design goal for Dylan was to provide the programmer with a
flexible dynamic language (hence, “Dylan”) for “rapid creation,
delivery, and subsequent modifications of (…) software.” Yet, to
satisfy the additional goals of being “practical on small machines”
and “achieve commercial performance in production code,” the
designers of Dylan introduced concepts that allow the programmer
to significantly limit the polymorphism of code, sealed classes
being the most important example. A sealed class cannot be
subclassed. Consequently, the class type of a sealed class is a
concrete type consisting of a single exact class: the sealed class
itself. Moreover, any method operating on a sealed class is
monomorphic. Concrete type information was gained, but at cost in
flexibility: sealed classes cannot be subclassed, and code
operating on their instances is monomorphic.
Thus, the designers of Dylan accepted giving up some flexibility to
improve deliverability.
|
|
|
|
|
|
|
Nick
Kramer
|
It becomes obvious after some use that the Dylan syntax was not
designed for short definitions and toy programs. Rather, it's meant for
large blocks of code with really-long-variable-names. And after a while of
writing large blocks of code with really-long-variable-names, one begins
to appreciate the syntax the way it is.
|
|
Martin Rodgers
|
Many such people try a simple "Hello, World" app and then give up. If
that were the only way to judge development software, we wouldn't even
be using C.
|
|
|
|
Ed Gamble
|
I'm sceptical that the syntax was a decision of Dylan's
designers. Look at their background and you've got to conclude
that the syntax came from someone with a business bent?
|
|
Scott E. Fahlman
|
Well, a lot of the Dylan designers are former Lispers who prefer
the elgance and simplicity of Lisp-like syntax. But many of us
are sick of being stuck in some specialized niche in the
programming language world and are willing to embrace infix if
that is what it takes to get mainstream acceptance Dylan.
|
|
Mark Richer
|
I think it's pragmatic to make Dylan seem as familiar to the
masses as possible, for example, by using a more familiar
syntax. But this shouldn't be taken to the extreme of denying
what's new or unique about it, particularly from the perspective of
the C, C++, Pascal, etc. programmer. The documentation
should, in fact, concentrate specifically on helping newcomers to
the language get the right "mental models" (assuming no
previous background in related languages).
|
|
|
|
Bruce Hoult
|
A goal of Dylan is to be able to split finished
programs out of the development environment
and make small binaries. That pretty much
precludes runtime operations on programs,
except in programs that exeute only within the
development environment, or carry very large
amounts of it with them.
|
|
Scott E. Fahlman
|
If basic things are broken up into libraries rather
than being a single monolithic system, it is
very important to keep the libraries consistent
and to keep track of what depends on what. A
hypercode environment can help here.
|
|
|
It's relatively easy to break chunks of the
language off into libraries, because the library
can define both new classes and new methods
to work on them. You don't have to decide in
advance whether the + operator should be
overloaded to support vector arithmetic, for
example, because a library can define both the
necessary classes and the methods that
extend the arithmetic operations to cover
these.
|
|
|
|
Oliver Steele
|
At the time we thought we needed to compete with Think C
and other emerging Windows/MacOS development
environments. (We also thought we needed to rival C's
efficiency; this took some IDE smarts to minimize
excruciatingly slow recompilations.) But the majority of
UN*X programmers never used such environments, and
none of the languages introduced subsequent to Dylan has
been accompanied by much in the way of an IDE, so it's
obviously not necessary.
|
|
|
|
Stephen Strom
|
When Dylan is called "complex," there doesn't seem to be an appropriate
distinction being made between "complex to implement" and "complex to
use." Dispatching on multiple arguments, for example, seems actually
simpler in concept than a specialized rule like you can only dispatch on one
argument.
Stroustrup has a fairly lengthy discussion of multiple-argument dispatch or
"multi-methods" in his book, _The Design and Evolution of C++_. He
rejected them "with regret," because of difficulty in implementation, not
because of any problems that he felt would arise with use. The recent
adoption of run-time type information, he says later in the book, would make
the implementation of multi-methods more feasible.
|
|
Scott E.
Fahlman
|
Dylan's syntax is more complex than Common Lisp's, but that's because
Dylan's designers thought it would make Dylan easier to learn and more
familiar to programmers who have worked in other programming languages
or who are already comfortable with infix notation for arithmetic.
Dylan's macro system will be more complex than Common Lisp's, but the
goal is to make it easier and safer for average programmers to do simple
things. Lisp macros are very elegant and powerful, but often require very
deep thinking on the part of the programmer to keep all the levels straight
and make everything work reliably.
|
|
|
|
|
|
|
Jason Pellerin
|
I still haven't made the mental leap Jasonfrom variables-in-c to variables- in-
dylan.
I probably shouldn't be thinking in terms of variables at all, but in terms of
objects, with a variable just being the name of some object
|
|
Mark Craig Chu-
Carroll
|
Variables are even weaker than that in Dylan.
A variable is a *temporary* name for an object. "let a = make(<gobble>)"
doesn't establish "a" as the name for the newly created object; it establishes
a temporary binding from the name "a" to the newly created object.
Reassigning a new value to the variable doesn't affect the old value in any
way.
|
|
|
|
Jason
|
Am I a backwards thinker,
with my brain stuck in C mode?
|
|
Mark Craig
Chu-Carroll:
|
That's pretty much what's going on.
I don't mean that to be insulting. The problem is, you're having a very hard time
grasping the ideas of Dylan, because you're trying to think about them in terms of
the concepts of more traditional languages like C and C++.
In my various attempts to explain Dylan to non-Lisp people, I've discovered that
the biggest hurdle is getting people to see past the ideas of pointers, and call-by-
reference versus call-by-value parameter passing. Those concepts are all
borrowed from the Algol family languages, which Dylan isn't part of.
In Dylan, you pass *objects* around as parameters. Down in the implementation,
that's probably implemented as pointers, but it doesn't have to be. The key thing
is that when you pass a parameter, you're actually passing an *object*, which
has identity. If you insist on thinking of it as a pointer, you're going to get
confused. You're not passing a *value*, and you're not passing a *pointer*; you're
passing an *object*.
An object comes with a set of methods, exported from its home module, that
allow you to perform operations on that object. The implementor of the object's
class may give you access to operations that alter the object in some way, if that
makes sense for the object. That's fine - those operations still preserve the
object's *identity*.
In C++, you worry about passing a pointer to an object because that gives you
the ability to alter the objects identity, by writing a new object into the memory
that was formerly used by the original object. You have the ability to alter its
*identity* through it's pointer. In Dylan, you can't do that. Object identity is
guaranteed to be preserved. The attributes of the object might be altered, but the
identity remains the same.
Without the ability to alter an objects identity, all that you can do to it are perform
the operations that the implementor of that object gave you access to. That is,
anything that you can do to alter an object from outside of its defining class is
something that the implementor decided was OK for you to do! You can't
"damage" the object, because the only things that you can do to it are the things
that are "safe" for the definition of the object.
For example, if you perform a function on a mutable set, you can add items to
that set. That's because it makes sense to be able to perform alterations on a
mutable set, so its mutation operations are accessible to you. But if you get a
non-mutable set, you won't be able to add elements to it, because the
implementor won't export any mutation operators that act on it.
So what it all really comes down to is you end up with a different model of safety.
In languages like Dylan, guarantees of safety are provided by the implementor of
the class in terms of the operations provided for acting on instances of the class.
That's sufficient for making a strong guarantee of safety in Dylan, because of its
preservation of identity. If you're using to a language like C++, it seems like that's
not enough - because in C++, to make a program safe, you need to worry about
preserving identity *as well as* the kinds of safety that you need in Dylan.
This all means that the style of programming in Dylan is radically different. Safe
programming styles in C++ are very value oriented - the operate more on values
than on objects. They copy things all over the place, and use value equality rather
than object equality for comparison. In C++, you design objects to be used that
way - you expect things to be copied like crazy. You write operations on objects
in ways that don't make any real effort to preserve identity, because you know
that no one is going to rely on it. In Dylan, you expect copying to be very rare,
and write your programs in a fashion that is strongly reliant on the preservation of
identity.
|
|
|
|
Seth
LaForge
|
I'm not sure how this generic function approach to OOP will turn out. Many
who use CLOS assure that it is good, but it will definitely take some mental
gear shifting for those of us used to a more object-based model.
|
|
Lyman S.
Taylor
|
Generic functions seem kind of like a natural idea once you try to combine
functional programming ( or treating functions a first class objects ) and OO
stuff.
Namely in OO you sort of organize the ADT into a is-a heirarchy of sorts
for example
<collection>
/
\
< array > <sorted-collection>
but everything in that heirarchy "is a " collection. But each has a slight
semantic difference too. The heirarchy goes from the most general down to
the most specific.
Generic Functions are way of grouping "like" functions with "mostly"
the
same semantics. Only they differ on the parameters they take. And
everything in this heirarchy has the same "name".
<function ( t t t ) >
/ \
<function (integer t t )> <function(character
t t)>
/
\
<function
\
( integer integer t )> \
< function ( charachter integer t ) >
The heirarchy is organized from least specific arguments to most specific
arguments.Just like a heirarchy of class goes from the most general to the
most specific.
However this heirarchy is reflects the heirarchy of the classes of the "data"
classes.
Generic function help enforce this semantic sameness of the all of these
functions by enforcing that they all take the same number of arguments.
I guess my point is that it is still object based. Only the syntax is slightly
more different than
object message
which gets kind of unwiedy once the message itself has arguments.
object message object object object
What's that? its not send a message to a object. Its send a message and
these objects. The specific message implied should depend upon types of
all the objects sent. So what's so strange about just flipping the order of the
first two.
message object object object object
I can see it coming.... "infix notation is more natural than
prefix notation.." :-)
However as outlined in the first part of this message you can still use the
member function" like style of OO in Dylan also.... the reference manual
doesn't particularly represent in neon lights though. ;-)
|
|
Peter
Norvig
|
One potentially confusing aspect of Dylan is that
someObject.myMethod(bunchOArgs)
is legal, but it probably doesn't do what you want. It is equivalent to
myMethod(someObject)(bunchOArgs)
which makes sense if myMethod(someObject) returns a function, which can
then be applied to bunchOArgs.
|
|
|
|
|
|
|
|
|
A Dylan Primer
for Scheme
Programmers
|
Almost everything you already do in Scheme can be translated easily into Dylan(tm).
In fact, with
one exception, Dylan is a proper superset of Scheme. The one exception is that continuations
have indefinite extent in Scheme and dynamic extent in Dylan. Some individual implementations of
Dylan might provide a mechanism for producing continuations with indefinite extent, making all
Scheme programs be Dylan programs, too. (Of course, you won't be taking full advantage of the
power of Dylan if you only write Scheme programs in it.)
This document is really just a large table in two columns. On the left, you will see
a Scheme
expression, and on the right, its Dylan counterpart. If one column contains N/A, then that language
has no corresponding direct way to express what is in the other column.
Please remember, there is much more to Dylan than what you'll find here! For example,
this
document doesn't show you how to define new classes or create generic functions (functions
which support ad hoc polymorphic behavior). This document is only intended to help ease
your
transition from Scheme to Dylan.
|
|
Scheme
|
Dylan
|
|
Literals
|
|
#t
|
#t
|
|
#f
|
#f
|
|
23
|
23
|
|
#b1011
|
#b1011
|
|
#o644
|
#o644
|
|
#x2A5F
|
#x2A5F
|
|
-4/5
|
-4/5
|
|
6.02E23
|
6.02E23
|
|
#\a
|
'a'
|
|
#\newline
|
'\n'
(Dylan supports
\a, \b, \e, \f,
\n, \r, \t, and \0)
|
|
"Hello"
|
"Hello"
|
|
N/A
|
"Hello\n"
|
|
'apple
|
#"apple" or apple:
|
|
N/A
|
#"two words"
(Think "hashed string"...)
|
|
'(1 #\a dog)
|
#(1, 'a', #"dog")
|
|
'#(5 10 15)
|
#[5, 10, 15]
|
|
`(1 2 ,x ,@y)
|
N/A
|
|
Syntax
|
|
|
Note that, in Dylan,
any words after an end (e.g.
end method) are optional.
|
|
(define var exp)
|
define variable var = exp
or
define constant var = exp
|
|
(f x y z)
|
f(x, y, z)
|
|
(begin 1 2 3)
|
begin 1; 2; 3; end
or
begin 1; 2; 3 end
|
|
(quote datum)
|
N/A
|
|
(lambda (x y . z)
(say "hello")
(f x y z)
)
|
method (x, y, #rest z)
say("hello");
f(x, y, z);
end method
|
|
(let ((x 5))
body)
|
let x = 5;
body
(Scope ends at next "body-ender.")
|
|
N/A
|
let (x, y) = exp;
(Binds multiple values returned by
exp.)
|
|
(let ((x 5) (y 6))
(f x y))
|
let (x, y) = values(5, 6);
f(x, y)
|
|
(letrec (
(f (lambda (x)
f-body)
)
(g (lambda (y z)
g-body)
))
body)
|
local method f (x)
f-body
end method f,
method g (y, z)
g-body
end method g;
body
|
|
(if test
(begin then1
then2)
(begin else1
else2)
)
|
if (test)
then1;
then2;
else
else1;
else2;
end if
|
|
(set! var value)
|
var := value
|
|
(and a b c)
|
a & b & c
|
|
(or a b c)
|
a | b | c
|
|
(cond
(test1 result1)
(test2 result2)
(else result) )
|
case
test1 => result1;
test2 => result2;
otherwise => result;
end case
|
|
(case exp
((a 2) result1)
(('a' 'b') result2)
(else result)
)
|
select (exp)
#"a", 2 => result1;
'a', 'b' => result2;
otherwise => result;
end select
|
|
N/A
|
select (exp by comparison-func)
f(x) => result1;
g(y), h(z) => result2;
otherwise => result;
end select
|
|
(do ((var1 init1 step1)
(var2
init2 step2))
(test
result)
body
)
|
for (var1 = init1 then step1,
var2 = init2 then step2,
until: test)
body
finally result
end for
|
|
Predefined functions
These
are organized based on the "Standard Procedures" section of R4RS.
|
|
(not obj)
|
~ obj or ~obj
|
|
(boolean? obj)
|
instance?(obj, <boolean>)
|
|
(eqv? x y)
|
x == y
|
|
(eq? x y)
|
N/A
|
|
(equal? x y)
|
x = y
|
|
(pair? obj)
|
instance?(obj, <pair>)
|
|
(cons x y)
|
pair(x, y)
|
|
(car ls)
|
head(ls)
|
|
(cdr ls)
|
tail(ls)
|
|
(set-car! ls val)
|
head-setter(val, ls)
or
head(ls) := val
|
|
(set-cdr! ls val)
|
tail-setter(val, ls)
or
tail(ls) := val
|
|
(cadadr ls)
|
N/A
|
|
(null? obj)
|
instance?(obj, <empty-list>)
//literally
or
obj = #()
or
empty?(ls) // the most common usage
|
|
(list? obj)
|
instance?(obj, <list>) //
approximately
|
|
(list x y z)
|
list(x, y, z)
|
|
(length ls)
|
size(ls)
|
|
(append ls1 ls2 ls3)
|
concatenate(ls1, ls2, ls3)
|
|
(reverse ls)
|
reverse(ls)
|
|
(list-ref ls n)
|
element(ls, n)
|
|
(member obj ls)
|
member?(obj, ls)
|
|
(memv obj ls)
|
member?(obj, ls, test: \==)
|
|
(= n1 n2) n1 = n2
or n1
== n2(symbol? obj)
|
instance?(obj, <symbol>)
|
|
(symbol->string sym)
|
as(<string>, sym)
|
|
(string->symbol str)
|
as(<symbol>, str)
|
|
(number? obj)
|
instance?(obj, <number>)
|
|
(complex? obj)
|
instance?(obj, <complex>)
|
|
(real? obj)
|
instance?(obj, <real>)
|
|
(rational? obj)
|
instance?(obj, <rational>)
|
|
(integer? obj)
|
instance?(obj, <integer>)
or
integral?(num)
|
|
(= n1 n2)
|
n1 = n2 or n1
== n2
|
|
(< n1 n2)
|
n1 < n2
|
|
(> n1 n2)
|
n1 > n2
|
|
(<= n1 n2)
|
n1 <= n2
|
|
(>= n1 n2)
|
n1 >= n2
|
|
(zero? n)
|
zero?(n)
|
|
(positive? n)
|
positive?(n)
|
|
(negative? n)
|
negative?(n)
|
|
(odd? i)
|
odd?(i)
|
|
(even? i)
|
even?(i)
|
|
(+ 1 2 3)
|
1 + 2 + 3
|
|
(* 1 2 3)
|
1 * 2 * 3
|
|
(- 5 3)
|
5 - 3
|
|
(/ 2.3 1.7)
|
2.3 / 1.7
|
|
(- x)
|
- x or -x
|
|
(expt 2 16)
|
2 ^ 16
|
|
[Most of the standard Scheme numeric functions (e.g.
max, remainder)
are defined simlarly in Dylan. I see no need to list them all here.]
|
|
(char? obj)
|
instance?(obj, <character>)
|
|
(char=? char1 char2)
|
char1 = char2 or
char1 == char2
|
|
(char<? char1 char2)
|
char1 < char2
|
|
(char>? char1 char2)
|
char1 > char2
|
|
(char<=? char1 char2)
|
char1 <= char2
|
|
(char>=? char1 char2)
|
char1 >= char2
|
|
(char->integer char)
|
as(<integer>, char)
|
|
(integer->char n)
|
as(<character>, n)
|
|
(char-upcase char)
|
as-uppercase(char)
|
|
(char-downcase char)
|
as-lowercase(char)
|
|
(string? obj)
|
instance?(obj, <string>)
|
|
(make-string k char)
|
make(<string>, size:
k, fill: char)
|
|
(string char ...)
|
N/A
|
|
(string-length str)
|
size(str)
|
|
(string-ref str k)
|
element(str, k) or
str[k]
|
|
(string-set! str k char)
|
element-setter(char, str, k)
or
str[k] := char
|
|
(string=? str1 str2)
|
str1 = str2
|
|
(string<? str1 str2)
|
str1 < str2
|
|
etc.
|
|
|
(substring str start end)
|
copy-sequence(str, start: start, end:
end)
|
|
(string-append str1 str2)
|
concatenate(str1, str2)
|
|
(string->list str)
|
as(<list>, str)
|
|
(list->string chars)
|
as(<string>, chars)
|
|
(string-copy str)
|
shallow-copy(str)
or
copy-sequence(str)
|
|
(string-fill! str char)
|
fill!(str, char)
|
|
(vector? obj)
|
instance?(obj, <vector>)
|
|
(make-vector k fill)
|
make(<vector>, size:
k, fill: fill)
|
|
(vector obj ...)
|
vector(obj, ...);
|
|
(vector-length vec)
|
size(vec)
|
|
(vector-ref vec k obj)
|
element(vec, k)
or
vec[k]
|
|
(vector-set! vec k obj)
|
element-setter(obj, vec, k)
or
vec[k] := obj
|
|
(vector->list vec)
|
as(<list>, vec)
|
|
(list>vector list)
|
as(<vector>, list)
|
|
(vector-fill! vec obj)
|
fill!(vec, obj)
|
|
(procedure? obj)
|
instance?(obj, <function>)
|
|
(apply proc arg1 arg2 args)
|
apply(proc, arg1, arg2, args)
|
|
(map proc list1 list2)
|
map(proc, list1, list2)
|
|
N/A
|
map(proc, vec1, vec2)
|
|
N/A
|
map(proc, string1, string2)
|
|
(for-each proc list1 list2)
|
do(proc, list1, list2)
|
|
Continuations
As I mentioned
before, continuations have dynamic extent in Dylan. Also, whereas call/cc is
a function, Dylan uses a syntax form to grab a continuation.
|
|
(call/cc
(lambda (k)
body))
|
block (k)
body
end block
|
|
(call/cc
(lambda (k)
(dynamic-wind
(lambda () #f)
(lambda ()
body)
(lambda ()
cleanup-stuff))))
|
block (k)
body
cleanup
cleanup-stuff
end block
|
|
Add
On
|
|
|
Semantics (for just one decl,
because I'm lazy)
let-values (
(IDENTIFIER EXP)) BODY) ==>
(let ((IDENTIFIER EXP)) BODY)
or, equivalently
(call-with-values
(lambda ()
EXP)
(lambda (IDENTIFIER)
BODY))
(let-values
((FORMALS* EXP)) BODY)
==>
(call-with-values
(lambda () EXP)
(lambda FORMALS* BODY))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Okay, here's a summary of the proposal I see coming
down the bend
(note that I don't like it, I'm just summarizing it
*smile*)...
Syntax:
EXP ::= (let-values (DECL ...) BODY)
| (let*-values
(DECL ...) BODY) ;; or let-values*?
DECL ::= (IDENTIFIER EXP)
|
(FORMALS* EXP)
FORMALS* ::= everything that the <formals>
nonterminal from R5RS
7.1.3 can be, _except_ for just an <identifier>.
|
|
This is probably the biggest difference between Dylan and Lisp.
|
Scott
McKay
|
you can start a Dylan application and, if it crashes, you
can start a debugging session on it over a "tether" (i.e.,
IPC to another process, RPC over the net to
another machine, etc) to debug it, even if you didn't
specially start the application with the intention of
debugging it.
Dylan does not include in the language runtime the
syntactic constructs of the language, nor does it include
a compiler in the run-time. This is probably the biggest
difference between Dylan and Lisp.
|
|
Neel
Krishnaswami
|
But Common Lisp
has a set of powerful features
Dylan doesn't. Reader macros, compiler macros,
and the ability to do computation at macro
expansion time. Dylan's macros are very close to
Scheme's; they are a hygienic pattern-expansion
mechanism (plus a feature to deliberately break
hygiene).
|
|
Paul F. Dietz
|
I was interested
in Dylan, but this makes me much
less interested. I use the power of CL's macros all
the time.
|
|
Neel
Krishnaswami
|
There's no shame
in not liking Dylan, of course, but I
still think it's very much worth trying out.
Personally, I think
much of the use of macros in Lisp
is to avoid using higher-order functions, or to work
around non-generic functions in the CL spec. These
are both more natural in Dylan. I don't want to over-
sell, though: if you are writing something like
Screamer, then CL is the only thing that can do it.
|
|
|
|
|
|
|