James--Thanks very much for clearing up the point about the source of the drawbacks I mentioned: OK: compilation unit size rather than number of passes. My subject line is misleading, then.
About the defrecord example in my itime #5: Ah, good--I didn't think of declaring ->Y. Thanks. Works with deftype too. Maybe this will allow me to avoid constraints on order of definitions within a file. I'll investigate that. The other points still seem like valid ones to me, whatever the reason is that the compiler doesn't allow explicit cyclic type dependencies. Even if the current compilation strategy is better for the very worthwhile goal of compatibility with the REPL (which, after all, has to compile one definition at a time), it still seems that it would be possible to provide a special compiler function or option that would allow cyclic dependencies, which users would have to invoke explicitly when needed. On Saturday, July 11, 2015 at 6:25:12 PM UTC-5, James Reeves wrote: > > 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 <mars...@logical.net <javascript:>> > 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 clo...@googlegroups.com >> <javascript:> >> Note that posts from new members are moderated - please be patient with >> your first post. >> To unsubscribe from this group, send email to >> clojure+u...@googlegroups.com <javascript:> >> 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+u...@googlegroups.com <javascript:>. >> 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.