> I recently gave a talk on Clojure to the LispNYC group. Audio and
> slides from the talk are now available online:
Since you say in your presentation slides that Clojure is still alpha,
I wonder if there's any chance that you would change some of the
names of standard functions and special forms? It's clear that
Clojure is now more Schemish than CLish, even though it
began life as a CL implementation before going in the
new direction that you describe (which I admire greatly).
That being so, it would make life easier for Scheme programmers
if Scheme names were used when they make sense:
#t instead of :t and discriminating between #f and nil,
equal? instead of eql? (which suggests CL's EQL, which it
is not), named-let rather than loop and recur, etc.
If this pushes your buttons, disregard it.
--
GMail doesn't have rotating .sigs, but you can see mine at
http://www.ccil.org/~cowan/signatures
> Not that I am against
> adding named-let/letrec, they may still appear, as I am not totally
> happy with thisfn.
Indeed. Don't you loose thisfn's use for a function the moment you
ender any nested function definitions? Also, mutual recursion can be
useful even without TCO (as long as you're careful).
What about adding a parallel let? There are some times it's useful,
and it's a hint to the reader that the definitions need not be
sequential.
> Clojure may be like Scheme in it's Lisp-1 nature, but it is more like
> CL in its treatment of nil and conditionals. I prefer conditionals to
> test for nil rather than false. If everything is conditional true,
> than 'nothing' should be conditional false (CL, Clojure). If there is
> a distinguished value for false, there should be a corresponding
> distinguished value for true (Java). Scheme tries to have it both
> ways. One could argue that it's just a naming thing - I could have
> called nil #f and it would be the same as Scheme. But Clojure is a
> hosted language and the host platform distinguishes null and false,
> and I have equated Clojure nil and Java null. In Clojure, nil means
> 'nothing/no object', and conditionals test for something/nothing.
>
> Empty collections are not nothing (so they are conditional true, like
> Scheme!), nor are they all the same thing (so, no Scheme null?, but
> could have empty?). Clojure seqs are logical streams and when they are
> consumed you are left with nothing, not an empty collection, so rest
> returns nil. I think it is extremely useful for rest to return
> conditional false (like CL). It leads to succinct and elegant idioms.
I agree that it makes some kind of sense for (rest '(1)) to return nil
(though I find (if (empty? s) a b) clearer than (if s b a)). What
about (first '()), (rest '()), (first nil), and (rest nil) though? I
those be contract violations. Otherwise it feels almost like every seq
has an infinite number of nils on the end =).
Also, currently I don't see an easy way of telling when a key is
mapped to nil in hash-tables. In PLT-Scheme the hash-table lookup
function takes an optional argument which is the value to return when
the key is not found. Should this be implemented?
(Also^2, if maps are seqs, I would expect to be able (key (first m))
and (value (first m)) to extract information from them. 'values' and
keys' don't seem really useful to me once that works. Currently it
seems that (first m) and (rest m) do not produce useful results.)
> eql? is not great. You're right, CLers might expect eql, and as far as
> Clojure and its data structures, eql? is mostly the same as Scheme
> equal? (although one could argue that the ability of equal? to return
> true for mutable objects in Scheme is an error - see
> http://home.pipeline.com/~hbaker1/ObjectIdentity.html). Unfortunately,
> I can't guarantee any semantics fully, because the real mapping is to
> Java's Object.equals(), which lets each class determine its own
> meaning. Clojure data structures define equals like Scheme equal? (or
> Baker's egal), but many Java classes do not (hmmm... actually many do
> follow egal in defaulting to mutability and reference equality). I
> guess the name should be 'equals', but I'd really like something much
> shorter. I wanted to use ==, but am afraid that Java programmers will
> expect a reference comparison. egal seemed too weird. Open to
> suggestions (but suggesters should read Baker's paper first, please).
Definitely have suggestions, but I'll read the paper first. Is the
java '==' exposed in clojure? '==' itself seems only to work on
numbers (of course this is incompatible, given that clojure use
by-reference numbers). Maybe the current '==' should be '=?' and a new
'==?' should expose the Java '=='?
On a slightly different note, looking at the comparators in boot.clj,
they don't seem to use recur. (apply == (repeat 1)) gives a stack
overflow instead of an endless loop. Of course, I doubt anyone would
ever want to do this, but they might want to apply == to arbitrarily
long seqs.
Henk Boom
Well, the main practical difference is when the variables you are
binding share names with old ones, so any example I could give of
parallel let usage could be made into a sequential let usage by
prepending all the names with 'new-' or similar.
The real reason I like parallel let is that it's symmetrical in that
all the values are evaluated in the same scope. I find this makes code
more understandable as you don't have to be worrying what effect the
early bindings in the let have on the later ones. It's a signal to the
reader as to what the value is based on.
> > What
> > about (first '()), (rest '()), (first nil), and (rest nil) though? I
> > those be contract violations. Otherwise it feels almost like every seq
> > has an infinite number of nils on the end =).
> >
>
> It's not a contract violation unless I promised otherwise :) That's
> the way it is in Common Lisp and I find it very practical. Whether it
> feels 'wrong' is mostly a matter of perspective.
Do you have an example of where it is useful to use first or rest on
nil? To me it feels like taking the car or cdr of the empty list (in
scheme this is a contract violation).
> > <feature request>
>
> Already on the todo list. Right now you can use contains if you are
> putting nils into maps.
>
> > <feature request>
>
> Also already on the todo list. Right now I am using my own IMapEntry
> but I am going to switch to implementing Map.Entry, and will provide
> key and val functions. Until then you can do this:
Cool!
> > Maybe the current '==' should be '=?' and a new
> > '==?' should expose the Java '=='?
> >
> I don't like Java's ==, will probably provide a separate identical?
> function and limit == to numbers.
Why no '?' if it is solely a predicate?
>
> =? is interesting as the core equality predicate.
>
Is this where egal goes? I thought eql? was going to be used this way
(as far as it can given some java classes' implementations of
equals()).
Henk Boom
I don't consider those huge problems, especially not allowing the
function to escape. However, it would be useful to be able to
recur an outer loop from within an inner loop, which named let
would allow (or does that not work for implementation reasons)?
> Clojure is a
> hosted language and the host platform distinguishes null and false,
> and I have equated Clojure nil and Java null. In Clojure, nil means
> 'nothing/no object', and conditionals test for something/nothing.
So when you call a Java method from Clojure code, you map nil
to false if the argument is boolean, and to null if it's an object?
(Theoretically there would be a problem if the argument is
java.lang.Boolean, I guess, since null would be valid.)
> Empty collections are not nothing (so they are conditional true, like
> Scheme!), nor are they all the same thing (so, no Scheme null?, but
> could have empty?).
Yes, I think an empty? predicate would be a good thing.
> I guess the short answer is there's no 'false' because that is not how
> Clojure works, and Clojure works differently than both CL and Scheme.
Most Schemes do provide a null separate from #f; in Chicken it's
the result of calling "void", and it's mapped to NULL in SQL and to
nil in Lua, both of which distinguish between false and null.
In other Schemes, you can usually capture it by evaluating
(if #f #f), though by the letter of R5RS this could return 47
or something.
> eql? is not great. You're right, CLers might expect eql, and as far as
> Clojure and its data structures, eql? is mostly the same as Scheme
> equal? (although one could argue that the ability of equal? to return
> true for mutable objects in Scheme is an error - see
> http://home.pipeline.com/~hbaker1/ObjectIdentity.html).
I thought about that quite a lot, and concluded that although Java equals()
is inconsistently implemented, it basically represents "contingent equality",
whereas egal represents "necessary equality". For example, two
StringBuffers are equals() in Java iff they happen to contain the same
characters at the moment of call, whereas because they are mutable
they could never be egal.
But it seems to me that equals() is mostly an upward compatible
extension of Scheme equal?, and should be so named. You could
only do egal in Java if there were some way of distinguishing value
classes from object classes, which there isn't. (On the .NET
platform, though, ...)
> I wanted to use ==, but am afraid that Java programmers will
> expect a reference comparison. egal seemed too weird. Open to
> suggestions (but suggesters should read Baker's paper first, please).
Yes, I think that would confuse the bejesus out of everyone. OTOH,
the name of Java == should be, I think, eqv? or just eq? (you can
make them the same, it's implementation-dependent), as that's
exactly what it does.
[later posting]
> > any example I could give of
> > parallel let usage could be made into a sequential let usage by
> > prepending all the names with 'new-' or similar.
>
> And probably should be renamed.
(rant "I and Dijkstra, and probably nobody else, believes that being able to
shadow outer variable names with inner ones is just a mistake, and
shouldn't be allowed. Python is sort of like this with the global
statement, but that's only when assignment is needed; I'd like to
say that *no* outer variable is available in an inner scope unless
(re)declared in that scope, for reasonably large values of
'scope'.")
> > Why no '?' if it is solely a predicate?
>
>
> == is a nice operator and people will expect it to do something, so it
> does what Java's does, for numbers only. Like Scheme's = has no "?"
Indeed, no Lisp has ever used ? on the numeric comparison
predicates. But I'd rather see = than ==. This is Lisp, after all, not
some language abortion that reserves = for assignment!
> > (Theoretically there would be a problem if the argument is
> > java.lang.Boolean, I guess, since null would be valid.)
>
> Not really - Booleans are not booleans.
Hmm. You don't autobox primitive types when calling Java methods,
then?
> Actually, 2 StringBuffers with identical contents are not equals(). If
> they were, I would consider them broken.
Sorry, you're right. I was thinking of Hashtables (or Maps generally),
which are equals() by contract iff they have the same mapping.
For that matter, the same is true of Lists and Sets.
> It's more than a philosophical point. IMO, egal or its moral
> equivalent is the only notion of composite value equality that has any
> utility in multithreaded programming,
I agree. (OTOH, I think that multithreaded programming with
shared data is an abomination.)
> I'll stick with eql? for now, and probably move to = at some point
> soon.
Okay.
> Are you talking about methods which take Integer, Boolean,
> Long et al wrapper classes explicitly?
Yes.
> Are there many?
Probably few or none.
> > I agree. (OTOH, I think that multithreaded programming with
> > shared data is an abomination.)
>
> Even immutable data?
No.