Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

You're dangerously close to trolling, but I'll comment anyway.

> a "I hate parens" Lisp-1

Clojure's minimal syntax is, in my opinion, a gigantic improvement over Common Lisp. And I say this as a person who quite likes parens. I feel similarly about Lisp-1s, but that is well trotted territory: Lisp-1s have won.

Parens are overloaded in traditional Lisps for both invocation and grouping. The introduction of vectors with square brackets makes a lot of code a great deal more readable at virtually zero cost: It's still homoiconic. Similarly, the inclusion of curly braces for maps is wonderful, as it makes maps a lot more common in Clojure than they would otherwise be in CL, which is a good thing for most business logic.

> replicatable in CL as a library

Defaults matter. A lot. Presence in a library is insufficient. The fact that seqs et al are in core means that 100% of Clojure libraries use those abstractions. That's a big deal.

> I don't understand why the libraries and macros to create similarly compelling interop over ABCL weren't developed instead

Rich discussed this on the CL mailing lists long before Clojure or even his first attempt, dotLisp, ever existed. See [1] and [2]. In short, interop needs to be planned for at the lowest levels to make it pleasant to use and efficient to execute. And it's not just JVM interop: Clojure was designed for $SOME_HOST interop, so ClojureScript interops as nicely with JavaScript as Clojure does with the JVM. CL would have two different libraries with two different ideas of interop for two different host platforms.

> I don't know why Clojure was created as a new language

I think you have a different definition of the word "language" than I do. Look at PG's Arc. It's built on a scheme implementation. It's not so much a new language by your definition as it is a set of scheme libraries. But that's what a language is: A common base vocabulary encoded with a well known set of syntax and semantics rules. Clojure could be implemented (quite trivially, thanks to the aforementioned careful hosting design) as a set of libraries to CL, but it would still be a new language.

[1] https://groups.google.com/d/msg/comp.lang.lisp/3-X76dTw8dI/I... [2] https://groups.google.com/d/msg/comp.lang.lisp/3-X76dTw8dI/s...

Snippet from [2]:

    If one were starting from scratch,
    and supplied a platform like .NET, would one define Lisp the way CL is
    defined?

    I would hope the answer is 'most definitely not'. For instance, .NET
    provides for numbers, characters, strings, arrays, hashtables, exceptions,
    namespaces, files, streams, user-defined types, a type hierarchy and
    inheritance, I/O, object creation and initialization, reflection etc. Should
    a language define its own incompatible versions of these things in such an
    environment?


"Parens are overloaded in traditional Lisps for both invocation and grouping."

And boy do I wish they were overloaded the same way in Clojure. I hate the fact that I have to say

     (cond
       (some-long-predicate involving multipler-parameters)
       (what-to-do has to go-on-the-next-line because-of wrapping)
       (here-is-the-next predicate)
       [(func1 arg1) ...])
It's very easy to end up in this situation (and it's not always just a sign that you need to refactor). It messes things up visually, and it also means that #_ doesn't work to comment out an entire test-and-expression (likewise #_ doesn't comment an entire binding-plus-binding-expression in a let), because it's not just one s-expression.

I don't really understand the rationale, tbh (in fact I don't even know what it's supposed to be to try to understand it); somewhere I saw the observation that there's no need for you as a macro writer to make the client of the macro use parens for grouping when you can just call (partition 2 ...) on the arguments, which is true enough (at least, as long as the unpaired args in a larger group (e.g. binding vector) or are the tail of the arguments to the macro, captured as a rest param), but ... I kind of doubt that CL and Scheme went with the form of let, cond, etc. that they did out of convenience for implementation of the relevant macros. And even if that were the original motivation, those forms have other benefits.


I'm not sure how your cond example is related to vector syntax. The decision to use (partition 2 ...) style pairs, rather than extra brackets is orthogonal.

The more important point of literal syntax for composites other than lists is the fact that they are resolved at read time. This means you don't need a macro to have grouping. With (some-macro ((f x) (g y))), you need to force expansion inside the parens. Quoting (some-function '((f x) (g x))) requires forcing evaluation in your function. You could use (vector (f x) (g x)), but now that means some-function gets a vector argument and some-macro would get a list argument with first element 'vector. In Clojure, (some-function-or-macro [(f x) (g y)]) both have a vector as an argument because of the read-time behavior of data structure literals. Huge win in my book.


The cond example isn't related to vector-style syntax. There are two options: either the unpaired elements are the last (so you can get them from rest parameters) or the unpaired elements are already grouped somehow (for instance, by being in a vector). Cond does the former, let does the latter. I'm not sure why you're mentioning vector syntax at all since the comment you're replying to was specifically talking only about grouping.

Literal syntax for composites other than lists is totally unrelated to this.

I actually really don't understand why if you had (some-macro ((f x) (g y))) you'd have to "force expansion inside the parens", since well-behaved macros won't expand their arguments. You'd iterate through the elements in the parens, but ... that's what you'd do with clojure-style partition macros, too, you'd just call partition first.

For a function call the obvious choice, and one you see in Scheme/Racket quite frequently, is either `(,(f x) ,(g y)) for your second example, or even (list (f x) (g y)). Yeah, then your macro gets a list whose first element is 'list, but ... so what? Your macro probably shouldn't be looking inside what it receives anyway, and if it does then it should be documented that it expects a literal ((foo bar) (baz quux)) or whatever (just as in Clojure you can't write (let (vector 'a 'b) (+ a b)) and have that work).


Actually, I'd appreciate expansion on the respect in which Clojure is designed for interop with an arbitrary host. One (relatively uninteresting) respect in which that might be the case is that Clojure-the-language might not say anything about, say, what numeric types there are. Actually it does, though: Clojure supports ... all Java numeric types. And then there are two more Clojure-specific types. Or perhaps that is the sense in which it's designed for interoperation: it just says "we'll use the number types the host has".

Isn't it the case that Clojure and ClojureScript have different libraries with "different ideas" (whatever that means) about interop with their hosts? And ClojurePy yet other ideas about interoperation with Python? The hosts are very different in each case; how could the interoperation not be? (I suppose you could supply the host with a runtime that emulates aspects of the interoperation on the original host---but that's not very interesting!)

(Also, he mentions dotLisp in the post you reference as footnote [1].)


I disagree that Numbers are relatively uninteresting. In fact, I think any language designer would tell you that numerics are one of the hardest areas to get right. It's also the area where Clojure is weakest in terms of interop, I'd say. ClojureScript, for example, only has host Numbers (ie double-precision floating-point)

The main thing that Clojure does in terms of being designed for interop is to require the use of host values/objects. There are no wrapper/proxy/delegate/whatever objects. This has big implications. It (unfortunately) means that there is behavior you simply can't have in core, such as reflection or continuations. That's a big part of being interop friendly: Intentionally specifying fewer things. If you have very strict rules for how something like strings behave, then you might wind up in a situation where you need to create a ClojureString class or something like that.

Additionally, the '. (dot) special form is like a giant "INTEROP HERE" sign, which is extremely useful when porting code from CLJ to CLJS. You can literally grep your source for /\./ and find platform specific code.


What I meant is that "if designed for interop" in the case of numbers is something like "there is no design for numbers, just use the host's numbers", that's not a very interesting design. And it leads to, for instance, huge differences between Clojure and ClojureScript (as you note).


Minimal syntax can be implemented in reader macros in Common Lisp. If you really think some punctuation marks are more readable than a LAMBDA form you can write a reader macro to do it (and people have).

Parens are not overloaded in Common Lisp. It's always (function value value ...). And lists are always (). It's an artifact of homoiconicity that the program code can be represented in the same first-class data structures that it manipulates. I think we can all agree that is an advantage.

   '()
Is just a reader macro for QUOTE which returns its arguments unevaluated. LIST simply returns a new list from its arguments. BACKQUOTE lets you optionally evaluate parts of a list (which is useful unsurprisingly in macro definitions).

Re: seq; I don't really see the advantage. Some defaults might be right for certain applications but I prefer to be in control of making that decision.

ABCL interop is working rather well. They even have CLOS support now.

Parenscript generates great Javascript from Common Lisp code. The great thing about Lisp-like languages is that the evaluator can take a description of a process and create a machine capable of emulating that process. This happens to be really useful when experimenting with new languages. There's even a cl-python project to evaluate Python code in Common Lisp.

The great idea of Common Lisp was to bring together under one standard the forest of incompatible lisp implementations. I'm happy to see experimentation in the lisp space but it seems that fragmentation is only going to happen if everyone runs off into the woods to write their own incompatible lisp over things like syntax.

As for $SOME_HOST interop... a fair bit of the language is an artifact of the JVM. LOOP/RECUR is simply because the JVM is probably never going to support TCO (and while not required by a CL implementation it's nearly universal because it makes for efficient recursive programs... a style of program that is rather natural to lisps). The language doesn't have conditions and restarts because of the JVM (though I've heard it posited that it could be possible to implement them on top of exceptions).


Wish I could upvote more than once.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: