[Prev][Next][Index][Thread]
Re: Does my ideal language exist?
-
To: info-dylan@ai.mit.edu
-
Subject: Re: Does my ideal language exist?
-
From: Bruce Hoult <bruce@hoult.org>
-
Date: Mon, 26 Jun 2000 01:00:02 -0400 (EDT)
-
Organization: The Internet Group Ltd
-
Posted-And-Mailed: yes
-
References: <m34s82daqz.fsf@enterprise.newedgeconcept> <394EB8F3.E4D35698@hotdispatch.com> <39500e18@news.sentex.net> <bruce-74B8B2.12334623062000@news.akl.ihug.co.nz> <m3pup89i2v.fsf@localhost.localdomain> <bruce-DE48E2.20035025062000@news.akl.ihug.co.nz> <m3d7l6x9fe.fsf@localhost.localdomain> <bruce-B32A94.03220426062000@news.akl.ihug.co.nz> <m3vgyxy2se.fsf@localhost.localdomain>
-
User-Agent: MT-NewsWatcher/3.0 (PPC)
-
Xref: traf.lcs.mit.edu comp.lang.misc:33278 comp.lang.forth:57141 comp.lang.clos:4994 comp.lang.dylan:12234
In article <m3vgyxy2se.fsf@localhost.localdomain>, Lieven Marchand
<mal@bewoner.dma.be> wrote:
> Bruce Hoult <bruce@hoult.org> writes:
>
> > In article <m3d7l6x9fe.fsf@localhost.localdomain>, Lieven Marchand
> > <mal@bewoner.dma.be> wrote:
> >
> > > Bruce Hoult <bruce@hoult.org> writes:
> > >
> > > > So it's not the infix syntax per se that you object to? Or, at
> > > > least,
> > > > it's hard to see how it could be if you don't like R5RS macros
> > > > either.
> > >
> > > No. I want an extensible language that allows me to design my own
> > > abstractions and expressing them in the most natural way. In order to
> > > have that in a sane way you need a syntax that doesn't get in the
> > > way.
> >
> > Surely Scheme has that?
> >
>
> Not really if you go by strict R^nRS. They've thrown out the concept
> of a reader and defined a program as a text string instead of as a
> list.
So, to you, Scheme is no longer a Lisp? (or never was?) Fair enough,
but clearly our most basic terms differ. I regard CL, Scheme, emacs
lisp, and Dylan as all being Lisps. Hell, Perl is damn near a Lisp
these days. They all have the lambda nature.
> > > I have nothing against infix per se but I have never seen a useful
> > > extensible language with infix syntax.
> >
> > I happen to think that Dylan is.
>
> Take a look at a recent thread in comp.lang.dylan named 'Crash course
> on macros?'. Something as conceptually simple as a COLLECTING macro
> turns into a for me completely unreadable mess of more than a page.
>
> (defmacro with-collect ((&rest collectors) &body forms)
> "Evaluate forms, collecting objects into lists.
> Within the FORMS, you can use local macros listed among collectors,
> they are returned as multiple values.
> E.g., (with-collect (c1 c2) (dotimes (i 10) (if (oddp i) (c1 i) (c2 i))))
> ==> (1 3 5 7 9); (0 2 4 6 8) [2 values]"
> (let ((ret (mapcar (lambda (cc) (gensym (format nil "~s-RET-" cc)))
> collectors)))
> `(let (,@ret)
> (macrolet ,(mapcar (lambda (co re) `(,co (form) `(push ,form
> ,',re)))
> collectors ret) ,@forms (values
> ,@(mapcar (lambda (re) `(nreverse ,re))
> ret))))))
>
> And that's for a simple thing.
The problem is that the "completely unreadable mess of more than a page"
provided a *lot* more functionality than your little macro above.
See what you think about the following. Exact same functionality as
yours, and I don't think it's any harder to understand, and perhaps
easier. It depends on what you're used to, I guess, but I don't have
any trouble following either (or writing the Dylan one, of course :-)
Even the length is about the same -- yours is nine lines as presented,
mine is eight. Obviously yours could be reformatted to use fewer (and
so could mine), or either could be perhaps made more understandable by
using a few more lines. I'm willing to call it a draw.
-----------------------------------------------------------------
module: collect
synopsis: make a "collect" macro corresponding to a given Lisp one
author: Bruce Hoult
define macro with-collect
{ with-collect (?:name, ?more-names:*) ?:body end }
=> {let vals = #();
local method ?name(item) vals := pair(item, vals) end;
let (#rest results) = collect (?more-names) ?body end;
apply(values, reverse!(vals), results);}
{ with-collect () ?:body end } => { ?body; values() }
end;
define function dotest()
with-collect (c1, c2)
for (i from 0 below 10)
if (odd?(i)) c1(i) else c2(i) end;
end;
end;
end dotest;
define function main()
let (odd, even) = dotest();
format-out("odd = %=, even = %=\n", odd, even);
end function main;
main();
-----------------------------------------------------------------
[bruce@k7 collect]$ time make;./collect
[... snip ...]
Compilation finished with 0 Warnings and 0 Errors
8.65user 0.75system 0:10.14elapsed 92%CPU (0avgtext+0avgdata
0maxresident)k
0inputs+0outputs (9495major+12263minor)pagefaults 0swaps
odd = #(1, 3, 5, 7, 9), even = #(0, 2, 4, 6, 8)
-----------------------------------------------------------------
That was with d2c, from www.gwydiondylan.org.
For your amusement, here is the intermediate C code for the entire
"dotest()" function. I've taken out only whitespace (incl comments).
The multiple value return code could stand some work, but I think you'll
find it's not too bad as is:
-----------------------------------------------------------------
descriptor_t * collectZcollectZdotest_FUN(descriptor_t *orig_sp)
{
descriptor_t *cluster_0_top;
descriptor_t *cluster_1_top;
heapptr_t L_vals; /* vals */
heapptr_t L_vals_2; /* vals */
long L_i; /* i */
long L_i_2; /* i */
descriptor_t L_temp;
descriptor_t L_temp_2;
heapptr_t L_instance; /* instance */
descriptor_t L_temp_3;
descriptor_t L_temp_4;
heapptr_t L_instance_2; /* instance */
heapptr_t L_arg1; /* arg1 */
descriptor_t L_temp_5;
heapptr_t L_results; /* results */
heapptr_t L_arg1_2; /* arg1 */
descriptor_t L_temp_6;
L_vals = dylanZempty_list.heapptr;
L_vals_2 = dylanZempty_list.heapptr;
L_i = 0;
while (1) {
L_i_2 = L_i;
if ((L_i_2 < 10)) {
if (((L_i_2 & 1) == 0)) {
L_temp.heapptr = collectZliteral.heapptr;
L_temp.dataword.l = L_i_2;
L_temp_2.heapptr = L_vals_2;
L_temp_2.dataword.l = 0;
L_instance = dylanZdylan_visceraZCLS_pair_MAKER_FUN
(orig_sp, L_temp, L_temp_2);
L_vals_2 = L_instance;
}
else {
L_temp_3.heapptr = collectZliteral.heapptr;
L_temp_3.dataword.l = L_i_2;
L_temp_4.heapptr = L_vals;
L_temp_4.dataword.l = 0;
L_instance_2 = dylanZdylan_visceraZCLS_pair_MAKER_FUN
(orig_sp, L_temp_3, L_temp_4);
L_vals = L_instance_2;
}
L_i = (L_i_2 + 1);
}
else {
goto block0;
}
}
block0:;
L_arg1 = dylanZdylan_visceraZreverseD_METH
(orig_sp, L_vals_2, dylanZliteral_67.heapptr);
L_temp_5.heapptr = L_arg1;
L_temp_5.dataword.l = 0;
cluster_0_top = orig_sp + 1;
orig_sp[0] = L_temp_5;
cluster_1_top = values_sequence
(cluster_0_top, dylanZliteral_7.heapptr);
cluster_0_top = cluster_1_top;
L_results = dylanZdylan_visceraZmake_rest_arg_FUN
(cluster_0_top, orig_sp + 0, cluster_0_top - orig_sp - 0);
L_arg1_2 = dylanZdylan_visceraZreverseD_METH
(orig_sp, L_vals, dylanZliteral_67.heapptr);
L_temp_6.heapptr = L_arg1_2;
L_temp_6.dataword.l = 0;
cluster_0_top = orig_sp + 1;
orig_sp[0] = L_temp_6;
cluster_1_top = values_sequence(cluster_0_top, L_results);
cluster_0_top = cluster_1_top;
return cluster_0_top;
}
-----------------------------------------------------------------
> CLOS has historically been implemented as a macro system without any
> support from the underlying Lisp. Could I build something like that
> with Dylan macros?
I honestly don't know, as I haven't seen the ones implementing CLOS.
> > CL and Scheme use () for every imaginable kind of grouping. Infix
> > languages tend to have different grouping tokens for function
> > arguments,
> > vector elements, sequential blocks of code, and control constructs.
> > This helps to reinforce the information gained form the other main
> > grouping cue: indentation.
> >
>
> Experienced Lispers don't even see (). They're for the
> editor. Indentation isn't only a cue in Lisp, it's everything. That's
> why there in in practice one way to indent Lisp source and anyone who
> doesn't follow that will be forcibly corrected by his colleagues.
Well, you can say that but it plainly isn't true. I mean, look at the
code you posted, with the () removed:
with-collect c1 c2 dotimes i 10 if oddp i c1 i c2 i
What does the indentation tell us here? Not a lot :-) Is it possible
to parse it? Yes, as Logo and FORTH have proved. But it's not very
pleasant.
> > The second advantage is the context gained by interspersing unique
> > "noise" tokens into things. For example, writing "for (i from 10 to
> > 100
> > by 3)" instead of (do ((i 10) (+ i 3)) (< i 100) ... ). This is
> > actually one of the best things about Smalltalk syntax, that I wish
> > would be adopted by more languages.
>
> So, build it with a macro ;-) Or in this case, use the macro defined
> for you by the nice ANSI CL committee:
>
> (loop for i from 10 to 100 by 3 summing i)
But that's *exactly* what Dylan *does*!!!
When I write "for (i from 10 to 100 by 3) ... end" in Dylan it is
actually transformed by a macro in the standard library into:
let init = 10;
let limit = 100;
let by = 3;
local method repeat(i)
block (return)
unless (i > limit)
...;
return(repeat(i + by));
end;
end;
end;
repeat(init);
The "block" and "unless" are further transformed by other macros before
you get down to the core Dylan language that the compiler proper
actually works with.
> My experience with Lisp is different and I find Dylan hard to read. I
> don't like the <> convention and a lot of the other Scheme like stuff
> like ? etc.
There are always matters of taste. I don't much like <> myself and
often don't do that in my own code. And I like the $var$ convention for
globals even less. If you were fanatical about it, you can always
rename all the system library stuff on import to something without the
<> around the class names.
But, really, isn't this once again a pretty trivial level to base a like
or dislike for a language upon? If you couldn't do your with-collect
macro in a reasonable way then that would certainly be something to
complain about, but minor naming conventions???
-- Bruce
Follow-Ups: