Re: Closures in macros
Am 05.05.2011 02:01, schrieb Ken Wesson: (There's an ugly workaround involving explicitly calling intern; you create a dummy namespace with a var holding the object, and then eval code that refers to that var by fully-qualified name in order to retrieve the object.) Yes, this is what I currently do. In the macro I do (let [f (gensym "logger")] (intern 'my.ns f @*logger*) `(~f ~level ...))) which pollutes the ns of my lib, but for now this works. Much better would be of course if the core team of Clojure would implement what you outlined. Greetings, André -- 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
Re: Closures in macros
Hello Ken, thanks for your explanations. It seems that you basically outlined a strategy that can be used to implement that feature. Very good! :-) Am 05.05.2011 02:21, schrieb Ken Wesson: As for concerns that this kind of extension might mask common macro errors, adding some *warn-on-foo* option to generate warnings when "unusual" kinds of object literal were encountered by eval/the compiler would hopefully address such. Yes, that’s what I thought too, when I read about Chris Perkins’ concerns. Let the default behaviour be an error message as it now is, but perhaps a more useful one, and let us have an option in defmacros attr-map or in a global switch to tell the compiler, or macroexpander here: “yes, I really want to embed a literal object in the code”. -- 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
Re: Closures in macros
On Wed, May 4, 2011 at 8:01 PM, Ken Wesson wrote: > Note that instantiating the class with 4 gives us an add-4-to function > instead of the original add-3-to function here. To actually convert > that particular closure into code that reproduces it, the array of > constructor arguments needs to be [{} 3]. (I'm not sure what the map > argument is used for, but it seems to work when left empty, though not > when omitted entirely. I did some more experimenting and the map attaches as metadata to the closure: user=> (def q (.newInstance (first (.getConstructors (.getClass (fn-maker3 2 1 (into-array Object [{:foo 1} 3 4]))) #'user/q user=> (q 10) 43 user=> (meta q) {:foo 1} There is also a second constructor for a closure that doesn't take a map. But it comes second in the sequence returned by .getConstructors. So you can use (second (.getConstructors ...)) with just the closures. Note that the fn-maker3 above was defined with: (defn fn-maker3 [x y] (fn [z] (+ (* z x) y))) and user=> ((fn-maker3 3 4) 10) 34 so it looks like the order of the constructor arguments can be rearranged from what you might expect given how the closure was defined. That wouldn't stop a clever compiler extension knowing the order needed (as it decided that order in the first place), saving them into the closure somehow (e.g. as metadata: {:recreate-args [4 3]}), and converting a closure into the code to recreate it (e.g. `(.newInstance (first (.getConstructors (class-for-name ~(.getName (.getClass closure) (into-array Object (quote ~(cons (meta closure) (:recreate-args (meta closure)) seems like it would evaluate to the required code, given the :recreate-args metadata, and it would preserve the metadata too). And since a non-closure also has two constructors -- besides the no-argument one, one accepting just a metadata map -- the above should in fact work to externalize any function object for reconstruction later (in any running session that has the function's class available) as long as the closed-over values (and the metadata) don't contain anything non-externalizable. In fact, if closure constructors only assoced the :recreate-args key into the closure's metadata automatically as an additional final step you could wrap the above in #=(eval ...) and use it to define print-dup for AFn. -- 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
Re: Closures in macros
On Wed, May 4, 2011 at 8:16 PM, Ken Wesson wrote: > Closures, including ones whose arguments can't be known until runtime, > can easily be slapped with "implements Serializable" if they haven't > been already. Indeed, an "extends Serializable" on IFn, on top of the > above compiler modification to externalize anything serializable, > would suffice to make it possible to eval (and even AOT-compile) code > containing any embedded function objects, save for closures that have > closed over non-serializable objects. One more clarification: *any* object could be eval'd. It would just be the case that if you tried to AOT compile code that contained (say, in the output of a macro expansion) a non-serializable, non-print-dupable object or a closure that had closed over one, the AOT compile would fail with a NotSerializableException (and the compiler could catch that and give a more helpful error message pointing to the offending line of Clojure code, so you could track down what object was making the AOT compile fail and fix it, either by replacing the literal object with code to recreate it or by adding a method to print-dup or slapping a Serializable on one of your Java classes or deftypes or whatever). As for concerns that this kind of extension might mask common macro errors, adding some *warn-on-foo* option to generate warnings when "unusual" kinds of object literal were encountered by eval/the compiler would hopefully address such. -- 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
Re: Closures in macros
On Wed, May 4, 2011 at 8:01 PM, Ken Wesson wrote: > Note that instantiating the class with 4 gives us an add-4-to function > instead of the original add-3-to function here. To actually convert > that particular closure into code that reproduces it, the array of > constructor arguments needs to be [{} 3]. (I'm not sure what the map > argument is used for, but it seems to work when left empty, though not > when omitted entirely. The compiler can always give closure objects > metadata amounting to the correct [{} 3] or whatever needed to > recreate the closure object by calling its class's constructor. Actually, this would require the arguments to be known at compile time. For (let [x 3] (fn [y] (+ x y))) this is possible, but for a return value from (defn adder-maker [n] (fn [y] (+ n y))) it's not. But what are we really doing here? There are just two situations where the compiler is being called. One is on-the-fly eval at runtime, and then the bytecode could just be built with code that works something along these lines: makeJavaClassWithNullPublicStaticFieldItUses(); TheNewClass.THE_PUBLIC_STATIC_FIELD = someValue; Here "someValue" would be an actual reference to the existing object in memory, and it will work with closures and any other object, even things like io streams backed by open file handles. The second is for AOT compilation of Clojure code. In that case, what we should probably aim for is that a) anything for which print-dup has a method and b) anything that implements Serializable can be externalized. For print-dupables you just print-dup to a string and generate a class whose static initializer puts that back in through the reader: something like static { someVar = RT.readString("(sorted-map [1 2 3 4])"); } The proof of concept for Serializables is similar: static { someVar = (new ObjectInputStream (new ByteArrayInputStream (new byte[] {0x1b, 0xff, 0x3a, 0xa7, ...}))).readObject(); } where the contents of the byte array literal are generated using ObjectOutputStream/ByteArrayOutputStream, natch. Closures, including ones whose arguments can't be known until runtime, can easily be slapped with "implements Serializable" if they haven't been already. Indeed, an "extends Serializable" on IFn, on top of the above compiler modification to externalize anything serializable, would suffice to make it possible to eval (and even AOT-compile) code containing any embedded function objects, save for closures that have closed over non-serializable objects. -- 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
Re: Closures in macros
On Wed, May 4, 2011 at 7:03 PM, Simon Katz wrote: > It is not possible to write out a function object (something returned > by lambda, Common Lisp's equivalent of fn) to a compiled file. No mix > of eval-when, separating functions and macros, and load order can > change that. The same is not true of Clojure functions, though. Any function object reference can be turned in bytecode into a constructor call to the appropriate class -- with a closure, passing the appropriate argument. For example: user=> (class map) clojure.core$map user=> (.newInstance (class map)) # user=> (.invoke (.newInstance (class map)) inc [1 2 3 4 5]) (2 3 4 5 6) So, to embed map (not 'map or 'clojure.core/map, but map itself) in (eval `(~map inc [1 2 3 4 5])) you could generate the bytecode for this Java: (new clojure.core$map()).invoke(reference-to-inc-function, reference-to-1-2-3-4-5-vector); With a closure, it's slightly more complicated: user=> (def q (class (let [x 3] (fn [y] (+ x y) #'user/q user=> q user$fn__1501$fn__1502 user=> (.invoke (.newInstance (first (.getConstructors q)) (to-array [{} 4])) 42) 46 Note that instantiating the class with 4 gives us an add-4-to function instead of the original add-3-to function here. To actually convert that particular closure into code that reproduces it, the array of constructor arguments needs to be [{} 3]. (I'm not sure what the map argument is used for, but it seems to work when left empty, though not when omitted entirely. The compiler can always give closure objects metadata amounting to the correct [{} 3] or whatever needed to recreate the closure object by calling its class's constructor. The original problem *can* reappear with one step of regress *if* the constructor arguments include something the compiler doesn't knows how to turn into bytecode -- i.e. (eval (atom {})) still won't work, and so neither will (eval (let [x (atom {})] (fn [y] (reset! x y, but (eval (let [x 3] (fn [y] (+ x y should work at that point as 3 is something that can already be turned into bytecode. Of course, for these trivial examples it's easy enough to turn the function or closure into a quoted expression that constructs it, such as (eval '(let [x 3] (fn [y] (+ x y. Where we run into problems is when we want to embed references to already-existing functions or other objects that lack global names. (There's an ugly workaround involving explicitly calling intern; you create a dummy namespace with a var holding the object, and then eval code that refers to that var by fully-qualified name in order to retrieve the object.) -- 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
Re: Closures in macros
On May 4, 11:31 pm, André Thieme wrote: > Am 04.05.2011 14:50, schrieb Simon Katz: > > >> For example Common Lisp does support this. > > > That's not true, or at least it's only partly true. > > > Here's a translation of your example into Common Lisp (I added a use > > of a# in the macro to avoid compiler optimization making the problem > > go away): > > > (defun f (x) (lambda () x)) > > (defparameter foo (f 0)) > > (defmacro bar () `(let ((a# ,foo)) a#)) > > (defun call-bar () (bar)) > > > I can compile this within a Lisp image (not compiling to file), and > > call call-bar with no problems. > > > But if I try to compile to file, compilation fails with the error > > Object # is of type FUNCTION which is not > > externalizable > > You need to wrap it into eval-when or separate functions and macros from > their use into different files and make sure the right load order is > used. Then this will work in CL. > > Andr It is not possible to write out a function object (something returned by lambda, Common Lisp's equivalent of fn) to a compiled file. No mix of eval-when, separating functions and macros, and load order can change that. Simon -- 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
Re: Closures in macros
On Wed, May 4, 2011 at 6:31 PM, André Thieme wrote: > You need to wrap it into eval-when or separate functions and macros from > their use into different files and make sure the right load order is > used. Then this will work in CL. Which are probably some of the reasons Clojure chose not to support the behavior. David -- 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
Re: Closures in macros
Am 04.05.2011 14:50, schrieb Simon Katz: For example Common Lisp does support this. That's not true, or at least it's only partly true. Here's a translation of your example into Common Lisp (I added a use of a# in the macro to avoid compiler optimization making the problem go away): (defun f (x) (lambda () x)) (defparameter foo (f 0)) (defmacro bar () `(let ((a# ,foo)) a#)) (defun call-bar () (bar)) I can compile this within a Lisp image (not compiling to file), and call call-bar with no problems. But if I try to compile to file, compilation fails with the error Object # is of type FUNCTION which is not externalizable You need to wrap it into eval-when or separate functions and macros from their use into different files and make sure the right load order is used. Then this will work in CL. André -- 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
Re: Closures in macros
Alessio Stalla writes: > The key point is that in Lisp "code" does not mean "text" [1]. Code is > made of data structures - lists, symbols, vectors, numbers, ... - and > macros are just functions that operate on those data structures. Hmm, interesting. One of the things that's drawn me to learning Clojure is the malleability of "code as data," but I hadn't thought about it as potentially running that deep before. Thanks for phrasing that in a way that clicked for me. > In other words, valid code is no longer determined by the reader (i.e. > the parser), but by the compiler and/or the interpreter, that take a > rich data structure as input. The reader is "only" useful to us humans > to enter code into the system. OTOH, the compiler still needs a defined interface. The data structures the reader is able to produce seem like a convenient definition for what data structures the compiler should meaningfully consume. Obviously the reader producing structures the compiler can't consume is useless, but the compiler consuming structures the reader can't produce seems equally problematic to me. It seems like code which doesn't have a textual representation would be much more difficult to reason about. Without guaranteeing a mapping from every valid compiler input to text which causes the reader to directly generate that input, it becomes impossible to inspect what the compiler is actually consuming. -Marshall -- 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
Re: Closures in macros
On May 4, 8:50 am, Simon Katz wrote: > > For example Common Lisp does support this. > > That's not true, or at least it's only partly true. > > Here's a translation of your example into Common Lisp (I added a use > of a# in the macro to avoid compiler optimization making the problem > go away): > > (defun f (x) (lambda () x)) > (defparameter foo (f 0)) > (defmacro bar () `(let ((a# ,foo)) a#)) > (defun call-bar () (bar)) > > I can compile this within a Lisp image (not compiling to file), and > call call-bar with no problems. > > But if I try to compile to file, compilation fails with the error > Object # is of type FUNCTION which is not > externalizable > > Common Lisp's notion of externalizable objects (when compiling) is > defined athttp://www.lispworks.com/documentation/HyperSpec/Body/03_bda.htm. > > Regards, > Simon Yes. And actually, it is sort of better to think about (lambda () x) { (fn [] x), in clojure} as the name of a function. Even though it is also a list, it is also the name of 'the function that returns x'. When trying to insert the literal function generated by (lambda () x), you generate an error, because you can't insert literal functions into macros, they aren't symbolic... only their names are. -- 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
Re: Closures in macros
On May 3, 5:22 pm, André Thieme wrote: > Am 02.05.2011 23:14, schrieb David Nolen: > > > The relevant clojure-dev thread. > >http://groups.google.com/group/clojure-dev/browse_thread/thread/f4907... > > > It's not clear whether the core team and the various contributors are > > interested in supporting the behavior you want. It's also not clear > > whether the issue is matter of the difficulty of a good implementation > > or simply disinterest. > > Thanks for that link. I would really like to hear (read) a statement of > one member of the core team. > Some of the limitations: > 1. (defmacro x [] `(let [a# ~(atom 0)])) > > 2. (defmacro y [] `(let [a# ~(comp inc inc)])) ; from that link > > 3. (defmacro z [] `(let [a# ~((fn [x#] (fn [] x#)) 0)])) > > All three calls fail, (x) and (y) and (z). > I see no plausible reason why it *should* be that way. > Instead this is useful and idiomatic lisp. > > Normally I wanted to write: > `(~(get @loggers level @logger) > ~(get *level-name* level) > ~domain ~msg ~e) > > The first get will see if for the given level there is a level > specific log function. If not it will fall back to the default logger > logger . So, loggers is a mapping from level to closures and > logger is a closure. Those closures are functions that take 4 args: > the log level, a domain, a message and an exception. > > Now I have to write instead > (let [l (get @loggers level @logger) > f (gensym "logger")] > (intern 'mylib.log f l) > `(~f ~(get *level-name* level) ~domain ~msg ~e)) > > which is plain ugly. I need to artificially invent a name, pollute the > NS of my logging lib and probably create less efficient code, because > a lookup of my gensymed ~f will be required at runtime. > Would be great if this could be fixed easily enough to get included in > a (near) future version. > > Regards, > Andre You can do this the original way you were trying to do if you change your closure factory into a 'closure-code-factory'. Essentially, instead of returning a (fn [x] ...), you want to return something that's been syntax-quoted (but of the same form). (You could make this pretty using a macro). Then you would insert this anonymous lambda wherever you need it, and call it inline (after binding with let or using apply). There needn't be any lookup and It shouldn't be any less efficient than what you are trying to do. Of course, you should note that the downside to this entire approach is that to change the logging settings, you have to recompile the entire program, which could get quite annoying. (Particularly if you wanted to turn logging on at a breakpoint or something). It might be better to take the hit on dereferencing, as it would be more useful, and then also include an option to eliminate all logging code by flipping a variable. -- 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
Re: Closures in macros
On 4 Mag, 16:29, "Marshall T. Vandegrift" wrote: > André Thieme writes: > > Please try this minimal example in your REPL: > > (defn f [x] (fn [] x)) ; the closure factory > > (def foo (f 0)) ; a useful instance > > (defmacro bar [] `(let [a# ~foo])) > > and then call (bar) > > I'm new to Clojure and don't have much experience with Lisps in general, > but trying to do this seems weird to me. My understanding is that > macros are, well, macros -- code which is expanded at compile-time to > other code. In which case the interface the macro facility provides > isn't a set of compiler hooks, but just the ability to factor and > abstract the same code you could write without the macro facility. What > ultimate code-expansion are you trying to achieve? Perhaps there's a > different way to do it which works within the confines of pure code > re-writing. The key point is that in Lisp "code" does not mean "text" [1]. Code is made of data structures - lists, symbols, vectors, numbers, ... - and macros are just functions that operate on those data structures. In other words, valid code is no longer determined by the reader (i.e. the parser), but by the compiler and/or the interpreter, that take a rich data structure as input. The reader is "only" useful to us humans to enter code into the system. Alessio [1] actually this is matter of debate, for example in the Common Lisp standard "code" is defined with both meanings, but without complicating things too much, my point is that the canonical form of the code - the one that is fed to the compiler - is not text. -- 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
Re: Closures in macros
> 3. (defmacro z [] `(let [a# ~((fn [x#] (fn [] x#)) 0)])) > > All three calls fail, (x) and (y) and (z). > I see no plausible reason why it *should* be that way. As it's been pointed out, the compiler won't re-compile compiled code. Those macros work if you don't unquote the expressions: (defmacro x [] `(let [a# (atom 0)] a#)) (defmacro y [] `(let [a# (comp inc inc)] a#)) (defmacro z [] `(let [a# ((fn [x#] (fn [] x#)) 0)] a#)) user=> @(x) 0 user=> ((y) 1) 3 user=> ((z)) 0 -- 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
Re: Closures in macros
André Thieme writes: > Please try this minimal example in your REPL: > (defn f [x] (fn [] x)) ; the closure factory > (def foo (f 0)) ; a useful instance > (defmacro bar [] `(let [a# ~foo])) > and then call (bar) I'm new to Clojure and don't have much experience with Lisps in general, but trying to do this seems weird to me. My understanding is that macros are, well, macros -- code which is expanded at compile-time to other code. In which case the interface the macro facility provides isn't a set of compiler hooks, but just the ability to factor and abstract the same code you could write without the macro facility. What ultimate code-expansion are you trying to achieve? Perhaps there's a different way to do it which works within the confines of pure code re-writing. -Marshall -- 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
Re: Closures in macros
> For example Common Lisp does support this. That's not true, or at least it's only partly true. Here's a translation of your example into Common Lisp (I added a use of a# in the macro to avoid compiler optimization making the problem go away): (defun f (x) (lambda () x)) (defparameter foo (f 0)) (defmacro bar () `(let ((a# ,foo)) a#)) (defun call-bar () (bar)) I can compile this within a Lisp image (not compiling to file), and call call-bar with no problems. But if I try to compile to file, compilation fails with the error Object # is of type FUNCTION which is not externalizable Common Lisp's notion of externalizable objects (when compiling) is defined at http://www.lispworks.com/documentation/HyperSpec/Body/03_bda.htm. Regards, Simon -- 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
Re: Closures in macros
On 4 Mag, 01:34, Chris Perkins wrote: > On May 3, 5:22 pm, André Thieme wrote: > > > Some of the limitations: > > 1. (defmacro x [] `(let [a# ~(atom 0)])) > > > 2. (defmacro y [] `(let [a# ~(comp inc inc)])) ; from that link > > > 3. (defmacro z [] `(let [a# ~((fn [x#] (fn [] x#)) 0)])) > > > All three calls fail, (x) and (y) and (z). > > I see no plausible reason why it *should* be that way. > > I do - because most of the time, embedding an object in code is > accidental, caused by messing up the quoting and unquoting. I would > prefer that evaling something that cannot be produced by the reader be > an error. > > I can see that in some cases, having eval pass unexpected types > through unchanged can be useful, but I think it would be much more > common for it to result in hard-to-debug errors, especially for > beginning macro writers. Code-is-data is the quintessential feature of Lisp. That some code cannot be produced by the reader is, at most, a limitation of the reader, not a reason for making that code illegal. It can, sure, be a source of errors, but no more than forgetting to quote or unquote a symbol in a macro expansion already is. Including closures in compiled classes is not trivial, but still technically doable, and since as André demonstrated that it's the basis for certain advanced macro techniques, imho it's a feature that should not be missing from a powerful, stable, widely used Lisp like Clojure. Alessio -- 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
Re: Closures in macros
On May 3, 5:22 pm, André Thieme wrote: > Some of the limitations: > 1. (defmacro x [] `(let [a# ~(atom 0)])) > > 2. (defmacro y [] `(let [a# ~(comp inc inc)])) ; from that link > > 3. (defmacro z [] `(let [a# ~((fn [x#] (fn [] x#)) 0)])) > > All three calls fail, (x) and (y) and (z). > I see no plausible reason why it *should* be that way. I do - because most of the time, embedding an object in code is accidental, caused by messing up the quoting and unquoting. I would prefer that evaling something that cannot be produced by the reader be an error. I can see that in some cases, having eval pass unexpected types through unchanged can be useful, but I think it would be much more common for it to result in hard-to-debug errors, especially for beginning macro writers. - Chris Perkins -- 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
Re: Closures in macros
Am 02.05.2011 23:14, schrieb David Nolen: The relevant clojure-dev thread. http://groups.google.com/group/clojure-dev/browse_thread/thread/f4907ebca8ef6e11 It's not clear whether the core team and the various contributors are interested in supporting the behavior you want. It's also not clear whether the issue is matter of the difficulty of a good implementation or simply disinterest. Thanks for that link. I would really like to hear (read) a statement of one member of the core team. Some of the limitations: 1. (defmacro x [] `(let [a# ~(atom 0)])) 2. (defmacro y [] `(let [a# ~(comp inc inc)])) ; from that link 3. (defmacro z [] `(let [a# ~((fn [x#] (fn [] x#)) 0)])) All three calls fail, (x) and (y) and (z). I see no plausible reason why it *should* be that way. Instead this is useful and idiomatic lisp. Normally I wanted to write: `(~(get @loggers level @logger) ~(get *level-name* level) ~domain ~msg ~e) The first get will see if for the given “level” there is a level specific log function. If not it will fall back to the default logger “logger”. So, “loggers” is a mapping from level to closures and “logger” is a closure. Those closures are functions that take 4 args: the log level, a domain, a message and an exception. Now I have to write instead (let [l (get @loggers level @logger) f (gensym "logger")] (intern 'mylib.log f l) `(~f ~(get *level-name* level) ~domain ~msg ~e)) which is plain ugly. I need to artificially invent a name, pollute the NS of my logging lib and probably create less efficient code, because a lookup of my gensymed ~f will be required at runtime. Would be great if this could be fixed easily enough to get included in a (near) future version. Regards, André -- 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
Re: Closures in macros
On Mon, May 2, 2011 at 4:49 PM, André Thieme wrote: > I am not interested in the answers of religious fanatics who defend any > behaviour that the current implementation has, even if it obviously > limits the expressiveness. > > > Regards, > André > The relevant clojure-dev thread. http://groups.google.com/group/clojure-dev/browse_thread/thread/f4907ebca8ef6e11 It's not clear whether the core team and the various contributors are interested in supporting the behavior you want. It's also not clear whether the issue is matter of the difficulty of a good implementation or simply disinterest. David -- 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
Re: Closures in macros
On Mon, May 2, 2011 at 4:49 PM, André Thieme wrote: > Maybe there are good reasons why closures should not be real first class > objects, as it is the case in other programming languages that support > them. If that is the case I would really like to hear it. > > I am not interested in the answers of religious fanatics who defend any > behaviour that the current implementation has, even if it obviously > limits the expressiveness. > > > Regards, > André > -- 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
Re: Closures in macros
Am 02.05.2011 02:26, schrieb Alan: You can't embed a function in code as a raw function object Can anyone give explanations why this is so? I understand that it may sound provocative, but this sounds to me like a major bug. - you need to return code that will result in that function. That it happens to work for non-closure functions is, as I understand it, a happy (or not?) accident. The "workaround" is simple enough: don't do it. This indeed sounds like a workaround. I don’t want to put you into the corner of the defenders of that current behaviour. So, please don’t take my comments as personal criticism. I just think that a functional programming language should support closures as first class objects everywhere, also in macros. Especially if that language is called… “Clojure”. For example Common Lisp does support this. In his book “Ansi Common Lisp” Paul Graham wrote: “With macros, closures, and run-time typing, Lisp transcends object- oriented programming.” A modern Lisp like Clojure should indeed support closures in macros. Instead, return code that will be evaluated in the expansion context and have the effect you want. The idea about “static logging” is that this is not required. I can of course dynamically look the current logger up and simply call (@*logger* lvl msg) But with static logging this would still result in dynamic code that can change its behaviour during runtime. What I want instead is to call the closure. Maybe there are good reasons why closures should not be real first class objects, as it is the case in other programming languages that support them. If that is the case I would really like to hear it. I am not interested in the answers of religious fanatics who defend any behaviour that the current implementation has, even if it obviously limits the expressiveness. Regards, André -- 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
Re: Closures in macros
On May 1, 4:42 pm, André Thieme wrote: > I am currently writing a neat logging lib and want it to support dynamic > and static logging. When I activate (static-logging!) then the log macro > (log :warn "message") will check during macro expansion time which log > level is currently set and decides based on that if code will be > generated that logs the message or not. > > The user can set any log function and for that purpose I offer some > closure factories, such as make-writer-logger or make-file-logger. > Two useful writer-loggers are defined as: > (def out-logger (make-writer-logger *out*)) > (def err-logger (make-writer-logger *err*)) > > The make-writer-logger returns a closure that takes a level and a > message and writes this to the given Writer. Now my current logger is > stored in an atom, for example the default is > (let [logger (atom out-logger)] …) > Inside my log macro I want to call @logger, which unfortunately results > in an Exception: > “No matching ctor found for class my.logger$make_writer_logger$fn__1588” > > Please try this minimal example in your REPL: > (defn f [x] (fn [] x)) ; the closure factory > (def foo (f 0)) ; a useful instance > (defmacro bar [] `(let [a# ~foo])) > and then call (bar) > > As soon f doesn’t return a closure but just a fn, then this Exception > is not thrown, but instead the code works. > > Just from looking at the macro definition I would say it should work for > any object as long foo is defined. > > Is that behaviour a bug? How easy is it to fix it? > Are there any work-arounds? You can't embed a function in code as a raw function object - you need to return code that will result in that function. That it happens to work for non-closure functions is, as I understand it, a happy (or not?) accident. The "workaround" is simple enough: don't do it. Instead, return code that will be evaluated in the expansion context and have the effect you want. user> (defmacro bar [[name] & body] `(let [~name (f 0)] ~@body)) #'user/bar user> (bar [log] (log)) 0 -- 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
Closures in macros
I am currently writing a neat logging lib and want it to support dynamic and static logging. When I activate (static-logging!) then the log macro (log :warn "message") will check during macro expansion time which log level is currently set and decides based on that if code will be generated that logs the message or not. The user can set any log function and for that purpose I offer some closure factories, such as make-writer-logger or make-file-logger. Two useful writer-loggers are defined as: (def out-logger (make-writer-logger *out*)) (def err-logger (make-writer-logger *err*)) The make-writer-logger returns a closure that takes a level and a message and writes this to the given Writer. Now my current logger is stored in an atom, for example the default is (let [logger (atom out-logger)] …) Inside my log macro I want to call @logger, which unfortunately results in an Exception: “No matching ctor found for class my.logger$make_writer_logger$fn__1588” Please try this minimal example in your REPL: (defn f [x] (fn [] x)) ; the closure factory (def foo (f 0)) ; a useful instance (defmacro bar [] `(let [a# ~foo])) and then call (bar) As soon f doesn’t return a closure but just a fn, then this Exception is not thrown, but instead the code works. Just from looking at the macro definition I would say it should work for any object as long foo is defined. Is that behaviour a bug? How easy is it to fix it? Are there any work-arounds? Thanks, André -- 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