Re: Understanding when compilation occurs - for function producing functions
Well, you misunderstand it, as far as I know, clojure treat all fn as object, so in your adder-maker example, clojure compiled two fn as object, that's adder-maker itself and anonymous function it returns. You must be right. As far as I know, the compiler is only called as part of eval, and I don't think eval would be called every time a function wants to produce a resultant function at run time. I guess what I'm trying to understand then, is how the maker function can produce a compiled function object when it doesn't know all the details of the function until runtime (the m parameter in this case). The compiled add-maker is just instantiate that anonymous with `m`, and return it. So (adder-maker 1) will produce that instantiated anonymous function, and since it's a function and it's in the first position of list, clojure will invoke it with 2, and that anonymous function will do actual computation. So maybe what is happening underneath is... When the function maker function is compiled, it creates a java object type corresponding to the anonymous function. This object type includes both an invocation member (taking one argument, n) and storage for m. Then at run time when this function is needed for a particular m (in this case 1), an instance of the java object type is created, with m set to 1. That's what I am guessing anyway. What this does mean though I guess is that the code can't be optimized to take advantage of the specific case of m being 1 (eg low level using an inc instruction on n rather than explicitly adding 1 to it). But maybe this is where JIT optimization comes into play - maybe this optimization can happen here? Thanks for your reply. Cheers, Mark. P.S. I still wonder whether the clojure compiler itself could do some kind of mini-compile when at-runtime producing a function. I guess it would be hard for the compiler to know whether the mini-compile was warranted or not. functions are compiled at once when clojure reader meet them, and invocation is just the matter of instantiate. Thanks, Di Xu -- 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.
Re: Understanding when compilation occurs - for function producing functions
What's the reason for asking? If you aim for making efficient code (that is running very many times in tight loops), I think most of this will be inlined by the JIT, as long as it is not confused by side effects and other things. I'm asking mostly because I want to better understand the essence of how clojure works. But you are right in thinking that I am also interested in better understanding performance implications - but this is a secondary reason. Primarily, I'm just trying to understand better. Coming from a C++ background I'm not that familiar with functions as first class values. We sort of do have them in C++ - as functors - ie a class that has the function invocation operator defined. This class can have storage as well, which means you can have a functor object type which then can have particular instances instantiated with different particular parameters stored with the object instance. I'm wondering whether this is effectively what Clojure does under-the-hood, or whether it does something different / more sophisticated. As long as the JVM can optimize the procedure, it's of lesser importance how Clojure solves it in one-shot scenarios. In one sense yes, though it's nice to better understand what is happening and how this influences program performance. Maybe I somewhat dodge the question, but dynamic just in time compilation is so mindblowing cool that an hypothesis about the quickest possible way to solve the problem combined with profiling is what matters most in practice. /Linus -- 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.
Re: Understanding when compilation occurs - for function producing functions
A toy project I've worked intermittently on makes heavy use of *partial* to dynamically build complex functions. I wish that *partial* was smart enough to recompile its first argument, maybe taking advantage of whatever type inference the compiler can make, but partial https://github.com/clojure/clojure/blob/028af0e0b271aa558ea44780e5d951f4932c7842/src/clj/clojure/core.clj#L2460 only returns a function wrapper. Ohwell. This raises a different question with me... Does clojure have the same scope for code optimization that other languages, eg C++, have? Ie, even though a C++ compiler has to deal with all sorts of complicated syntax, it is able to perform a whole lot of at-compile-time optimizations based on the C++ semantics. Is the clojure compiler free to do a similar thing? Or is it bound to adhere to the straightforward evaluation rules? My understanding is that one of the roles of macros is to perform at-compile-time optimizations. Sometimes a macro can do something clever to transform less performant code into optimized code. But is this the only way of improving compiled code - ie introducing code-optimizing macros - or does the clojure compiler itself have scope to elide certain things and make other performance optimizations? I know the java JIT optimization reduces the need for this, but my guess is that there is still a role for at-compile-time code optimization. Cheers, Mark. -- 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.
Re: Understanding when compilation occurs - for function producing functions
Coming from a C++ background I'm not that familiar with functions as first class values. We sort of do have them in C++ - as functors - ie a class that has the function invocation operator defined. This class can have storage as well, which means you can have a functor object type which then can have particular instances instantiated with different particular parameters stored with the object instance. I'm wondering whether this is effectively what Clojure does under-the-hood, or whether it does something different / more sophisticated. Okay. Functions as values. Go look at the IFn interface, https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/IFn.java. In C or C++ you can take a raw machine pointer to a function. The JVM does not allow for this behavior the result of which is that whenever you have a function really what you have is a function-like object with the standard application/invocation member methods. Nothing really sophisticated here, a single class is naively generated for each fn in your program and a fn that returns a fn simply creates a new instance of that fn and returns it since there's no other way that we can take a function as a value prior to JVM 1.8 which has bytecode lambdas and which the reference Clojure implementation doesn't leverage yet if ever. The standard Clojure compiler is pretty braindead when it comes to the emitted bytecode, but this is due to the philosophy (backed up by experience) that the JIT is typically good enough. You could generate better code, and my GSoC project is research into doing so but the reality of Clojure programs is that function calls even with Var indirection are free in comparison to the performance hits we take due to using immutable datastructures and eschewing in place updates. Reid -- 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.
Re: Understanding when compilation occurs - for function producing functions
2014-06-22 9:12 GMT+02:00 Reid McKenzie rmckenzi...@gmail.com: since there's no other way that we can take a function as a value prior to JVM 1.8 which has bytecode lambdas and which the reference Clojure implementation doesn't leverage yet if ever. Java 8 gained no such feature. Lambda expressions are syntax sugar for almost the thing, clojure's fn does: Instantiating an anonymous inner class for an interface with a single arity. The only concept that jvm has of a first class function, are the interfaces in java.util.function, as of Java 8 Sorry for the nitpick! Concerning more extensive compiler optimizations: The simplistic approach of the compiler has the benefit of generating pretty predictable byte code, which is good for a lot of cases, as long as the JIT can optimize it and as long as you don't need fast startup. The JIT can do pretty amazing stuff like inlining heap allocations onto the stack, if they don't escape. So in theory that could eliminate lazy-seqs, partial functions and other wrappers. I'd also like to see more extensive optimization by the clojure compiler, but I wouldn't pass the burden of implementing them onto anyone, before clojure.tools.{analyzer,emitter} are used as the canonical compiler. cheers -- 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.
Re: Understanding when compilation occurs - for function producing functions
Okay. Functions as values. Go look at the IFn interface, https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/IFn.java. Thanks for the link - this helps! When the clojure compiler generates a class type that conforms to this interface, does it generate a .java file which is then compiled using the java compiler? I am guessing not. I am guessing that clojure knows how to generate a byte code version of a class type directly. In C or C++ you can take a raw machine pointer to a function. The JVM does not allow for this behavior the result of which is that whenever you have a function really what you have is a function-like object with the standard application/invocation member methods. Thanks for explaining. This sounds very much like a C++ functor. For the record, the raw-pointer-to-function is the old-school thing to do in C++. These days functors are often used - object types that have the function application operator defined (which is a method with special function application syntax) - because these are more powerful (eg templated methods allowing compile-time polymorphism) and allow for better performance (eg inlining of code). So it sounds like what clojure does under-the-hood is to more-or-less define a functor type at compile time and then do various instantiations at run time. The standard Clojure compiler is pretty braindead when it comes to the emitted bytecode, but this is due to the philosophy (backed up by experience) that the JIT is typically good enough. Good to know - thanks! This makes things clearer for me. You could generate better code, and my GSoC project is research into doing so but the reality of Clojure programs is that function calls even with Var indirection are free in comparison to the performance hits we take due to using immutable datastructures and eschewing in place updates. Reid -- 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.
Re: Understanding when compilation occurs - for function producing functions
Okay. Functions as values. Go look at the IFn interface, https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/IFn.java. Thanks for the link - this helps! When the clojure compiler generates a class type that conforms to this interface, does it generate a .java file which is then compiled using the java compiler? I am guessing not. I am guessing that clojure knows how to generate a byte code version of a class type directly. The Clojure compiler generates .class files and Java bytecode directly, using the ASM http://asm.ow2.org/index.html library. Note that Clojure code is always compiled to Java bytecode, even in the REPL; Clojure is never directly interpreted. There are some good resources on how Clojure compilation works: http://clojure.org/compilation http://www.deepbluelambda.org/programming/clojure/how-clojure-works-a-simple-namespace (namespaces also get compiled to Java classes, which is a bit less intuitive) http://stackoverflow.com/questions/3123662/compiling-clojure (if you want to play around with invoking the Clojure compiler directly) -- 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.
Re: Understanding when compilation occurs - for function producing functions
On Sunday, June 22, 2014 11:35:25 AM UTC-5, Herwig Hochleitner wrote: 2014-06-22 9:12 GMT+02:00 Reid McKenzie rmcke...@gmail.com javascript: : since there's no other way that we can take a function as a value prior to JVM 1.8 which has bytecode lambdas and which the reference Clojure implementation doesn't leverage yet if ever. Java 8 gained no such feature. Lambda expressions are syntax sugar for almost the thing, clojure's fn does: Instantiating an anonymous inner class for an interface with a single arity. The only concept that jvm has of a first class function, are the interfaces in java.util.function, as of Java 8 I think (but would be happy to be corrected) that Java 8 lambdas *are* actually a little different than an anonymous class instantiation. Specifically, I believe the lambda is created via an invokeDynamic and cached at the call site, whereas the anonymous class instance would be constructed every time. Alex -- 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.
Re: Understanding when compilation occurs - for function producing functions
Suppose at my REPL I do... (defn direct-report-oneplustwo [] (println (str Direct one plus two is ((fn [n] (+ 1 n)) 2) .))) ...then I presume that the compiler has compiled my direct-report-oneplustwo function, and that this has included compilation of my anonymous function (fn [n] (+ 1 n)). So that now if I run... (direct-report-oneplustwo) ...at my REPL it just needs to run this precompiled code to tell me my answer 3 - no additional compilation required. So far so good. But what if instead I do this at my REPL... (defn adder-make [m] (fn [n] (+ m n))) (defn indirect-report-oneplustwo [] (println (str Indirect one plus two is ((adder-make 1) 2) .))) ...Now at this point, both adder-make and indirect-report-oneplustwo should have been compiled. But unlike our first approach, the anonymous function (fn [n] (+ 1 n)) has not yet been created by (adder-make 1) and so presumably (??) has not yet been compiled?? I am presuming that it is only when we actually run the report function... (indirect-report-oneplustwo) ...that (adder-make 1) is actually run, thereby producing the anonymous function (fn [n] (+ 1 n)), which then gets compiled right then-and-there, immediately followed by its execution?? Well, you misunderstand it, as far as I know, clojure treat all fn as object, so in your adder-maker example, clojure compiled two fn as object, that's adder-maker itself and anonymous function it returns. The compiled add-maker is just instantiate that anonymous with `m`, and return it. So (adder-maker 1) will produce that instantiated anonymous function, and since it's a function and it's in the first position of list, clojure will invoke it with 2, and that anonymous function will do actual computation. functions are compiled at once when clojure reader meet them, and invocation is just the matter of instantiate. Thanks, Di Xu -- 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.
Re: Understanding when compilation occurs - for function producing functions
On Saturday, June 21, 2014, Di Xu xudi...@gmail.com wrote: Suppose at my REPL I do... (defn direct-report-oneplustwo [] (println (str Direct one plus two is ((fn [n] (+ 1 n)) 2) .))) ...then I presume that the compiler has compiled my direct-report-oneplustwo function, and that this has included compilation of my anonymous function (fn [n] (+ 1 n)). So that now if I run... (direct-report-oneplustwo) ...at my REPL it just needs to run this precompiled code to tell me my answer 3 - no additional compilation required. So far so good. But what if instead I do this at my REPL... (defn adder-make [m] (fn [n] (+ m n))) (defn indirect-report-oneplustwo [] (println (str Indirect one plus two is ((adder-make 1) 2) .))) ...Now at this point, both adder-make and indirect-report-oneplustwo should have been compiled. But unlike our first approach, the anonymous function (fn [n] (+ 1 n)) has not yet been created by (adder-make 1) and so presumably (??) has not yet been compiled?? I am presuming that it is only when we actually run the report function... (indirect-report-oneplustwo) ...that (adder-make 1) is actually run, thereby producing the anonymous function (fn [n] (+ 1 n)), which then gets compiled right then-and-there, immediately followed by its execution?? Well, you misunderstand it, as far as I know, clojure treat all fn as object, so in your adder-maker example, clojure compiled two fn as object, that's adder-maker itself and anonymous function it returns. The compiled add-maker is just instantiate that anonymous with `m`, and return it. So (adder-maker 1) will produce that instantiated anonymous function, and since it's a function and it's in the first position of list, clojure will invoke it with 2, and that anonymous function will do actual computation. functions are compiled at once when clojure reader meet them, and invocation is just the matter of instantiate. Thanks, Di Xu What's the reason for asking? If you aim for making efficient code (that is running very many times in tight loops), I think most of this will be inlined by the JIT, as long as it is not confused by side effects and other things. As long as the JVM can optimize the procedure, it's of lesser importance how Clojure solves it in one-shot scenarios. Maybe I somewhat dodge the question, but dynamic just in time compilation is so mindblowing cool that an hypothesis about the quickest possible way to solve the problem combined with profiling is what matters most in practice. /Linus -- 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.
Re: Understanding when compilation occurs - for function producing functions
A toy project I've worked intermittently on makes heavy use of *partial* to dynamically build complex functions. I wish that *partial* was smart enough to recompile its first argument, maybe taking advantage of whatever type inference the compiler can make, but partial https://github.com/clojure/clojure/blob/028af0e0b271aa558ea44780e5d951f4932c7842/src/clj/clojure/core.clj#L2460 only returns a function wrapper. Ohwell. -- 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.