Clojure's compilation strategy isn't a matter of single-pass vs multi-pass, but of what constitutes a compilation unit.
In Clojure, a compilation unit is a top-level form. Within a form, you can have cyclic references: (letfn [(f [x] (if (> x 0) (g (- x 2)) :f)) (g [x] (if (> x 0) (f (- x 1)) :g))] (f 9)) I believe Clojure's compiler is technically a two-pass compiler. So your question isn't so much about single-pass vs. multi-pass compilers, but a question about how large a compilation unit should be. Should a compilation unit be a top-level form, or an entire file, or an entire project? Currently Clojure considers a top-level form to be a compilation unit. A file has no inherent semantic meaning to the compiler; it's just a way of storing a sequence of top-level forms. Changing this isn't a case of giving Clojure a two-pass compiler (and it has one already anyway), but significantly changing how Clojure thinks about compilation. As for why it chooses this compilation strategy to begin with, I believe it's because it makes dealing with REPLs and macros somewhat more straightforward. Regarding your specific example, you can get around it just by using the constructor functions: (defprotocol XP (make-Y [this])) (defprotocol YP (make-X [this])) (declare ->Y) (defrecord X [x] XP (make-Y [this] (->Y x))) (defrecord Y [y] YP (make-X [this] (->X y))) - James On 11 July 2015 at 20:31, Mars0i <marsh...@logical.net> wrote: > My understanding is that all of the drawbacks mentioned below are a > consequence of Clojure using a single-pass compiler. Feel free to correct > my misunderstandings, which no doubt exist. > > > 0. Provisos: I don't want Clojure to include every feature that anyone > wants. I don't even want it to include every feature that I want. The > feature that I most want is what Clojure provides, which is a small set of > elegant, flexible functions that allow one to do a lot with a little bit of > code, without the language being cluttered up by a confusing set of > non-orthogonal, confusingly named features. I'm immensely grateful to Rich > and others who've worked on the design and implementation of Clojure. The > follow is not an attack or a criticism, but a statement of reasons for what > seems like a small change in a language that I love using. > > > 1. The world contains cyclic dependencies. Not all relationships are > hierarchical. > > 2. Fortunately, Clojure allows cyclic dependencies. They're completely > unproblematic in many cases due to dynamic typing. Clojure only forbids > cyclic dependencies when the dependency *explicitly* involves something > that can be interpreted as a Java class, as defined, for example, by > defrecord. This seems like an arbitrary distinction. > > 3. Allowing cyclic dependencies to work in some cases but not in others is > confusing, and can lead to puzzling behavior: > > - If I add type hints, what was not a forbidden cyclic dependendency can > suddenly become one and fail to compile. > > - If I add forbidden cyclic dependency to one source file, and that source > file depends on a file that's already been compiled, the first, > newly-modified source file may compile and the program may run without > trouble--until I delete the second class file or or try to compile on a > different machine on which some of the source files have not been compiled > yet. (So I can give my coworker code that worked on my machine but breaks > on hers, even though we have the same source code and use the same > libraries. I may also have made other code depend on the recently-added > functions that embody the cyclic dependency; when I realize what's gone > wrong, I'll have to go back and fix all of that code that depends on the > offending functions in order to fix the problem.) > > 4. One shouldn't shouldn't have to organize code into source files so that > conceptually very distinct things are required to be in the same file, or > at least in the same namespace, in order to avoid forbidden cyclic > dependencies. One shouldn't have to create huge source files > unnecessarily. And whether code has to be organized like that shouldn't > depend on whether type hints are added: One shouldn't have to turn > well-organized code into poorly organized code just for the sake of > optimization. (That should be the compiler's job.) > > 5. Some cyclic dependencies won't compile even if all of the code is in > one file. If I have functions that refer to each other, I can add a > declare statement, but there's no way to do the same thing for types. The > following won't compile no matter how I rearrange the definitions: > > (ns Foo) > (defprotocol XP (make-Y [this])) > (defprotocol YP (make-X [this])) > (defrecord X [x] XP (make-Y [this] (Y. x))) > (defrecord Y [y] YP (make-X [this] (X. y))) > > A related point is that it may be necessary to rearrange code in a file in > awkward, unintuitive ways in order to get it to compile. This compounds > the problem of putting a lot of code in one file. > > 6. Changing to two-pass compilation wouldn't add any functions or > datatypes to the language, so there's no sense in which it clutters up the > language. And it's hard to believe that a two-pass compiler would be very > slow compared to the existing compiler, although I don't know much about > compilers. From the programmer's point of view, changing to a single-pass > compiler is a trivial change, and breaks no code. > > These points concern ways in which coding in Clojure becomes inconvenient, > awkward, or bug-prone. In another language, I might not mind as much. I > might just think that programming requires care and skill, and I would take > pride in my ability to work around such problems, and in the specialized > knowledge I've gained that allows me to do so. With Clojure, I expect > more, I guess. > > Honestly, I don't expect the single-pass compilation to go away, but I > don't understand why it persists. The preceding points should be pretty > obvious to Rich and other members of the core Clojure team. I know that > some of them have been made before, but I haven't seen all of them made. > > -- > You received this message because you are subscribed to the Google > Groups "Clojure" group. > To post to this group, send email to clojure@googlegroups.com > Note that posts from new members are moderated - please be patient with > your first post. > To unsubscribe from this group, send email to > clojure+unsubscr...@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/clojure?hl=en > --- > You received this message because you are subscribed to the Google Groups > "Clojure" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to clojure+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.