1.3 How to help newcomers to the language get the right "mental models?
1.3.1 What is the core feature of Dylan? Object-oriented function calls, not objects
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.
paradigma shift side notes

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. :-)
1.3.2 Dylan language design principles
provide abstraction facilities as well as performance
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. :-)
readability before writability
Bob Cassels
on Goo-Mail-Archive
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.
flexibility versus deliverability
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.
syntax
Designed for large blocks of code with really-long-variable-names
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.
Designed with infix syntax to reach the mainstream
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).
Break basic things up into libraries
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.
IDE
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.
1.3.3 Is Dylan complex?
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.
1.3.4 What metal leaps are required
from variables-in-c to variables-in-dylan; from pointer reference to object reference
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.
Am I a backwards thinker, with my brain stuck in C mode?
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.
How to do the mental gear shift single-dispatch OOP to multi method dispatch?
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.
1.3.5 What are the key aspects during the design of Dylan?
1.3.6 What is your background?
For Scheme Programmers
written by Jonathan Sobel and quoted from http://www.cs.indiana.edu/~jsobel/forschemers.html
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>.

For LISP Programmer
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.