Free book on Clojure (in German)
If you speak German, I have a free book on Clojure (Funktionale Programmierung in Clojure) for you: http://denkspuren.blogspot.de/2013/04/freies-clojure-buch-funktionale.html Dominikus -- -- 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/groups/opt_out.
Clojure and the Anti-If Campaign
Three weeks ago I stumbled across the Anti-If Campaign ( http://www.antiifcampaign.com/). An instant later I realized that one could easily re-implement if in Clojure with maps. More interestingly, polymorphic functions can be easily motivated with the help of maps. And this naturally leads to multimethods. If you like, enjoy reading my blogpost on The root of polymorphism: The Anti-If Campaign. It might be an interesting read for Clojure enthusiasts. http://denkspuren.blogspot.de/2012/05/root-of-polymorphism-anti-if-campaign.html Cheers, Dominikus -- 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
Bug recognizing tail position as default value in maps?
Is suspect this being a bug: When a recursive function call is used as a default value in a map, its tail position is not recognized. The problem can be easily demonstrated using the fixpoint function. The fixpoint function is usually defined as (defn fix [f x] (let [v (f x)] (if (= v x) x (recur f v To avoid the 'if' special form, we can use a map. The compiler rejects compilation and does not recognize (recur f v) to be correctly in tail position. (defn fix2 [f x] (let [v (f x)] ({x x} v (recur f v CompilerException java.lang.UnsupportedOperationException: Can only recur from t ail position, compiling:(NO_SOURCE_PATH:41) Avoiding tail recursion lets fix2 compile but not execute properly: (defn fix2 [f x] (let [v (f x)] ({x x} v (fix2 f v #'user/fix2 (fix identity 1) 1 (fix2 identity 1) StackOverflowError user/fix2 (NO_SOURCE_FILE:44) Strange, isn't it!? Dominikus -- 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: Bug recognizing tail position as default value in maps?
Sure? The semantics of the default value corresponds to a 'if', doesn't it? From this viewpoint, the default value is in tail position. And why does the non-tailrecursive version not run as expected? Dominikus Am Freitag, 27. April 2012 16:45:44 UTC+2 schrieb Meikel Brandmeyer (kotarak): Hi, (defn fix2 [f x] (let [v (f x)] ({x x} v (recur f v recur is not in the tail position. The call to the map is the tail call. So the result is as expected. Kind regards, Meikel -- 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: Bug recognizing tail position as default value in maps?
I got it, guys! Thanks a lot! This non-tailrecursive version works as intended: (defn fix2 [f x] (let [y (f x)] (eval ({x x} y (list 'fix2 f y) Dominikus Am Freitag, 27. April 2012 17:00:52 UTC+2 schrieb Luke VanderHart: Using a map instead of if means that it is evaluated as a function call. Unlike the if form, function calls eval their arguments. So the (recur) form is getting eval'd prior to being passed to the map/function, which isn't a tail position. That's why if is a special form/macro, not a regular function (like maps are). On Friday, April 27, 2012 10:52:10 AM UTC-4, Dominikus wrote: Sure? The semantics of the default value corresponds to a 'if', doesn't it? From this viewpoint, the default value is in tail position. And why does the non-tailrecursive version not run as expected? Dominikus Am Freitag, 27. April 2012 16:45:44 UTC+2 schrieb Meikel Brandmeyer (kotarak): Hi, (defn fix2 [f x] (let [v (f x)] ({x x} v (recur f v recur is not in the tail position. The call to the map is the tail call. So the result is as expected. Kind regards, Meikel -- 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
Contagious BigInts in 1.3? Why is (type (- 2 1N)) java.lang.Long?
In Clojure 1.3, BigInts are said to be contagious across operations. When different types of numbers are used in a math operation, the result will be the larger or more general of the two types. For example, any integer operation involving a BigInt will result in a BigInt, [...]. http://dev.clojure.org/display/doc/Documentation+for+1.3+Numerics However (type (- 2 1N)) results in java.lang.Long and does not seem to work accordingly. What's going on here? Cheers, Dominikus -- 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: Contagious BigInts in 1.3? Why is (type (- 2 1N)) java.lang.Long?
Thanks, Eric. My original problem was the factorial function, which breaks despite using BigInts: user= (defn fact [n] (if (= n 1N) 1N (* n (fact (- n 1N) #'user/fact user= (type (fact 1)) clojure.lang.BigInt user= (type (fact 20)) java.lang.Long user= (type (fact 21)) ArithmeticException integer overflow clojure.lang.Numbers.throwIntOverflow (Num bers.java:1374) The optimization to using Longs seems to be too aggressive. Upgrade casting in Clojure 1.2 is cool and simple. Dominikus On Sep 8, 1:30 pm, Eric Lavigne lavigne.e...@gmail.com wrote: You have discovered a very recent change. The idea is to automatically switch to longs for performance when it is clear that overflow will not occur. https://github.com/clojure/clojure/commit/684fca15040e1ec8753429909b2... There are still some problems with this optimization, which will hopefully be worked out before the final 1.3 release. http://dev.clojure.org/jira/browse/CLJ-836 On Thu, Sep 8, 2011 at 6:25 AM, Dominikus dominikus.herzb...@gmail.com wrote: In Clojure 1.3, BigInts are said to be contagious across operations. When different types of numbers are used in a math operation, the result will be the larger or more general of the two types. For example, any integer operation involving a BigInt will result in a BigInt, [...]. http://dev.clojure.org/display/doc/Documentation+for+1.3+Numerics However (type (- 2 1N)) results in java.lang.Long and does not seem to work accordingly. What's going on here? -- 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: Contagious BigInts in 1.3? Why is (type (- 2 1N)) java.lang.Long?
Right, this feature is documented in http://dev.clojure.org/display/doc/Documentation+for+1.3+Numerics For me it feels somewhat strange to use primed operations to enforce upgrade casting and to write a special faculty function for that. I just discovered that (fact 21) breaks while (fact 21N) does not but (fact 22N) does: user= (defn fact [n] (if (= n 1) 1 (* n (fact (- n 1) #'user/fact user= (fact 21) ArithmeticException integer overflow clojure.lang.Numbers.throwIntOverflow (Num bers.java:1374) user= (fact 21N) 5109094217170944N user= (fact 22N) ArithmeticException integer overflow clojure.lang.Numbers.throwIntOverflow (Num bers.java:1374) Weird! Dominikus On Sep 8, 2:05 pm, Meikel Brandmeyer (kotarak) m...@kotka.de wrote: Hi, Am Donnerstag, 8. September 2011 13:57:49 UTC+2 schrieb Dominikus: Upgrade casting in Clojure 1.2 is cool and simple. It's also in 1.3 cool and simple. user= (defn fact [n] (if (= n 1N) 1N (* n (fact (- n 1N) #'user/fact user= (fact 21) ArithmeticException integer overflow clojure.lang.Numbers.throwIntOverflow (Numbers.java:1374) user= (defn fact' [n] (if (= n 1N) 1N (*' n (fact (dec n) #'user/fact' user= (fact' 21) 5109094217170944N Sincerely Meikel -- 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: Contagious BigInts in 1.3? Why is (type (- 2 1N)) java.lang.Long?
Thanks for the explanation, Meikel! I'll wait for the next release which hopefully fixes the bug. Cheers, Dominikus On Sep 8, 2:31 pm, Meikel Brandmeyer (kotarak) m...@kotka.de wrote: Hi, Am Donnerstag, 8. September 2011 14:21:09 UTC+2 schrieb Dominikus: Right, this feature is documented in http://dev.clojure.org/display/doc/Documentation+for+1.3+Numerics For me it feels somewhat strange to use primed operations to enforce upgrade casting and to write a special faculty function for that. This is the trade-off between safe/fast and safe/slow. I just discovered that (fact 21) breaks while (fact 21N) does not but (fact 22N) does: Weird! This is due to the fact, that the overflow in (fact 21) happens in the *. With (fact 21N) this works because the 21N promotes the * to a BigInt operation and (fact 20) still fits into a long. So everything works here. With (fact 22N) the (- 22N 1) demotes the 21 to a long (-- Here is the bug) since it small enough to fit in there. Then we are in the (fact 21) situation again. Please keep in mind, that this behaviour is due to a bug in the current BigInt implementation! Normally a (fact 21N) would just work. The BigInt should use long arithmetic internally if possible. But the re-promotion to a real BigInt doesn't work correctly. Sincerely Meikel -- 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: Why do transactions block each other?
Right, it's according to the spec, but it doesn't explain the behavior. as I noticed this morning http://clojure.org/refs references http://en.wikipedia.org/wiki/Multiversion_concurrency_control (the technique used to implement STM) which provides an explanation for the experienced behavior: quote If a transaction (Ti) wants to write to an object, and if there is another transaction (Tk), the timestamp of Ti must precede the timestamp of Tk (i.e., TS(Ti) TS(Tk)) for the object write operation to succeed. Which is to say a write cannot complete if there are outstanding transactions with an earlier timestamp. /quote That's why the second transaction, which comes later in time, is the one waiting for the first transaction to complete. Dominikus On Aug 30, 9:53 am, Laurent PETIT laurent.pe...@gmail.com wrote: 2011/8/30 Kevin Downey redc...@gmail.com the two threads race to acquire the write lock and the winner runs, the loser retries. my guess is acquiring the write lock helps avoid live locks between transactions. Yes, and after re-readinghttp://clojure.org/refs, the current behaviour does not seem to violate any of the 10 guaranteed listed points. So, while it seems unfortunate from client's code point of view, it is correct wrt to the current specs. Now I more deeply understand why one should at all price avoid long computations from within a transaction ... Cheers, -- Laurent On Mon, Aug 29, 2011 at 10:59 PM, Laurent PETIT laurent.pe...@gmail.com wrote: My tests were false. Since I sent the code at once to the REPL, there was a race condition between the start of the agent and the dosync for setting the ref's value to 2000 ; this explains the weirdness of my test results (thank you, Mr Murphy, for having made the tests look like beta2 behaved differently from the others ...) So now I see consistent behaviours across 1.2.0, beta1 and beta2. But I am still perplex and confused by the behaviour ... 2011/8/30 Laurent PETIT laurent.pe...@gmail.com ok so now i'm totally confused, since I cannot see why the behaviour is so different between beta1 and beta 2 ... 2011/8/30 Laurent PETIT laurent.pe...@gmail.com Congratulations, you've found a bug in clojure 1.3 beta2, see: look with my clojure 1.2.0 version : ;; Clojure 1.2.0 = (def thread (agent Thread)) (def account (ref 1000)) (send thread (fn [agt aref] (dosync (alter aref + 100) (Thread/sleep 8000) agt)) account) (time (dosync (ref-set account 2000))) #'user/thread #'user/account #Agent@7e543cb1: Thread Elapsed time: 0.498106 msecs 2000 = ;; 10 seconds later : = @account 2100 And now with clojure 1.3 beta1 : ;; Clojure 1.3.0-beta1 = (def thread (agent Thread)) (def account (ref 1000)) (send thread (fn [agt aref] (dosync (alter aref + 100) (Thread/sleep 8000) agt)) account) (time (dosync (ref-set account 2000))) ;; 10 seconds later : #'user/thread #'user/account #Agent@6bf51e5c: Thread Elapsed time: 0.270225 msecs 2000 = @account 2100 And now with Clojure 1.3-beta2 ;; Clojure 1.3.0-beta2 = (def thread (agent Thread)) (def account (ref 1000)) (send thread (fn [agt aref] (dosync (alter aref + 100) (Thread/sleep 8000) agt)) account) (time (dosync (ref-set account 2000))) #'user/thread #'user/account #Agent@50fba502: Thread Elapsed time: 7957.328798 msecs 2000 ;; 10 seconds later : = @account 2000 ;; 10 more seconds later : = @account 2000 2011/8/30 Laurent PETIT laurent.pe...@gmail.com 2011/8/29 Dominikus dominikus.herzb...@gmail.com Thanks a lot for this detailed analysis and the pointers to the Java implementation, Stefan! That's excellent and very helpful. I still wonder, why the first transaction blocks write access. My overall understanding of the transaction system is that changes to referenced values remain in-transaction before committing them, a write-lock shouldn't be needed. I'm surprised, that the second transaction is the one who has to retry 71 times even though the first transaction hasn't committed anything yet. I must confess this behaviour also challenges my assumptions. Does this work the same whatever the clojure version used ? Cheers, Dominikus P.S.: Thanks for the idea to use 'future' to spawn a thread! On Aug 29, 5:42 pm, Stefan Kamphausen ska2...@googlemail.com wrote: The call to alter already wrote to the Ref, this requires a write-lock on the ref (see LockingTransaction.java/doSet). After that it sleeps a while and gets to its commit phase. The other transactions retries in the meantime. Consider the following code, which introduces some atoms for counting the retries (defn tz [] (let [rr (ref
Re: Why do transactions block each other?
That's a cool link and an interesting read! Thanks, Stefan! Dominikus On Sep 5, 10:23 pm, Stefan Kamphausen ska2...@googlemail.com wrote: Hi, there is another paper in the wild which very nicely describes, how Clojure's implementation of STM works and how it compares to other management strategies for resolving conflicts. http://vbn.aau.dk/files/32587755/report.pdf Definitely worth a read, I think. Best, Stefan -- 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: Why do transactions block each other?
Thanks a lot for this detailed analysis and the pointers to the Java implementation, Stefan! That's excellent and very helpful. I still wonder, why the first transaction blocks write access. My overall understanding of the transaction system is that changes to referenced values remain in-transaction before committing them, a write-lock shouldn't be needed. I'm surprised, that the second transaction is the one who has to retry 71 times even though the first transaction hasn't committed anything yet. Cheers, Dominikus P.S.: Thanks for the idea to use 'future' to spawn a thread! On Aug 29, 5:42 pm, Stefan Kamphausen ska2...@googlemail.com wrote: The call to alter already wrote to the Ref, this requires a write-lock on the ref (see LockingTransaction.java/doSet). After that it sleeps a while and gets to its commit phase. The other transactions retries in the meantime. Consider the following code, which introduces some atoms for counting the retries (defn tz [] (let [rr (ref 10) a1 (atom 0) a2 (atom 0)] (println Starting future) (future (dosync (swap! a1 inc) (alter rr + 100) (Thread/sleep 8000))) (println Sleeping a bit) (Thread/sleep 1000) (println Another dosync) (time (dosync (swap! a2 inc) (ref-set rr 1))) [@rr @a1 @a2 (.getHistoryCount rr)])) user (tz) Starting future Sleeping a bit Another dosync Elapsed time: 7001.554 msecs [1 1 71 0] Note, how the 71 retries nice fit the approximately 7 seconds left and the 100 miliseconds time-out (LOCK_WAIT_MSECS) for the lock used in LockingTransaction.java/tryWriteLock. Transactions with significantly different run-times should be avoided, although there is some point at which older transactions will get their turn. This could be another explanation of the effect. Take a look at the barge-function in LockingTransaction.java Hope this helps, Stefan -- 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: Eval destroys equality
On May 6, 8:39 pm, Adam Burry abu...@gmail.com wrote: On May 6, 3:10 pm, Andy Fingerhut andy.finger...@gmail.com wrote: Caveat: The following fact may already be well known to those discussing this issue, and I may not be clear on the goal of this effort. If the goal is to have functions compare as equal whenever they are equivalent in some sense, then that is an undecidable problem, even if the two functions are restricted to pure functions with no side effects and closed over no vars. The proof is nearly the same as that for the halting problem. Of course, it is still decidable to get the correct answer for some restricted kinds of functions. If that is what you are after, go for it. I don't think anyone expects quick sort to compare equal to bubble sort or anything like that. I think the issue is to get two functions with the same textual representation to compare equal. Adam Right, Adam. As Armando pointed out, Clojure 1.3 at least doesn't fail in the case I brought up. I would be very interested to learn what has changed in the Clojure 1.3 source code (compared to 1.2) to achieve this. Dominikus -- 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
Eval destroys equality
My observation is best distilled with the following definition of a function in Clojure 1.2: user= (defn id [x] (list id x)) #'user/id Interstingly, (id 7) and (eval (id 7)) result in different instances of function id as the number after the '@' char unveils: user= (id 7) (#user$id user$id@53797795 7) user= (eval (id 7)) (#user$id user$id@2de12f6d 7) Consequently, the following comparison leads to false: user= (= (id 7) (eval (id 7))) false Why is the instance relevant to '='? What is the precise semantics of two values being equal in Clojure? Dominikus (Remark: In Scheme, the use of 'eqv?' returns also #f, but the less restrictive 'equal?' does not and returns #t.) -- 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: Eval destroys equality
Thanks a lot, Armando. Looks like I should switch to Clojure 1.3 asap. Cheers, Dominikus 2011/5/5 Armando Blancas armando_blan...@yahoo.com: In 1.3 the function will (eval) to itself: Clojure 1.3.0-alpha6 user= (defn id [x] (list id x)) #'user/id user= (id 7) (#user$id user$id@3411a 7) user= (eval (id 7)) (#user$id user$id@3411a 7) user= (= (id 7) (eval (id 7))) true On May 5, 6:04 am, Dominikus dominikus.herzb...@gmail.com wrote: My observation is best distilled with the following definition of a function in Clojure 1.2: user= (defn id [x] (list id x)) #'user/id Interstingly, (id 7) and (eval (id 7)) result in different instances of function id as the number after the '@' char unveils: user= (id 7) (#user$id user$id@53797795 7) user= (eval (id 7)) (#user$id user$id@2de12f6d 7) Consequently, the following comparison leads to false: user= (= (id 7) (eval (id 7))) false Why is the instance relevant to '='? What is the precise semantics of two values being equal in Clojure? Dominikus (Remark: In Scheme, the use of 'eqv?' returns also #f, but the less restrictive 'equal?' does not and returns #t.) -- 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 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: Eval destroys equality
Thanks for the pointers to the implementation, Jonathan! Unfortunately, I couldnt' find out yet, which part of the source code in Clojure 1.3 is responsible for fixing the misbehavior in 1.2. The parts you point to haven't changed in 1.3. Cheers, Dominikus On May 5, 4:27 pm, Jonathan Fischer Friberg odysso...@gmail.com wrote: = uses the clojure.lang.Util/equiv to compare two things. The source of this function is: [1] static public boolean equiv(Object k1, Object k2){ if(k1 == k2) return true; if(k1 != null) { if(k1 instanceof Number k2 instanceof Number) return Numbers.equiv(k1, k2); else if(k1 instanceof IPersistentCollection k2 instanceof IPersistentCollection) return ((IPersistentCollection)k1).equiv(k2); return k1.equals(k2); } return false; } Which says: if k1 and k2 is the same instance (has the same id), return true if k1 and k2 are numbers, compare them as numbers if k1 and k2 are collections, compare them as collections otherwise, use the function equals of the k1 object. The equals function defines intelligent (proper) comparison of two objects, and is defined by the programmer. If the equals function isn't defined, it behaves like == [2] The == function returns true if the things are of the same instance. I think functions in clojure are defined in [3], but I'm not entirely sure. As you can see, equals isn't implemented, and thus = will only compare instance (id), as you have noticed. Jonathan [1]https://github.com/richhickey/clojure/blob/master/src/jvm/clojure/lan... https://github.com/richhickey/clojure/blob/master/src/jvm/clojure/lan... [2]http://leepoint.net/notes-java/data/expressions/22compareobjects.html [3]https://github.com/richhickey/clojure/blob/master/src/jvm/clojure/lan... On Thu, May 5, 2011 at 3:04 PM, Dominikus dominikus.herzb...@gmail.com wrote: My observation is best distilled with the following definition of a function in Clojure 1.2: user= (defn id [x] (list id x)) #'user/id Interstingly, (id 7) and (eval (id 7)) result in different instances of function id as the number after the '@' char unveils: user= (id 7) (#user$id user$id@53797795 7) user= (eval (id 7)) (#user$id user$id@2de12f6d 7) Consequently, the following comparison leads to false: user= (= (id 7) (eval (id 7))) false Why is the instance relevant to '='? What is the precise semantics of two values being equal in Clojure? Dominikus (Remark: In Scheme, the use of 'eqv?' returns also #f, but the less restrictive 'equal?' does not and returns #t.) -- 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 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: Evaluation of Symbol Bindings vs. Special Forms
You are right, Ken. I was a little bit sloppy regarding macro expansion. The macroexpand1 method (line 5275) called by analyzeSeq quits immediately, if the operator is a special symbol. Thus, 'if' cannot be overwritten by a macro as you demonstrated with your if- defmacro example, only the use of a qualified name can. Regarding your qqq-defmacro example, you seem to be onto something. From reading the code in Compiler.java, I don't get (yet), why this does not work as expected, but there is a comment left in method analyze saying in line 5155: // todo symbol macro expansion? Maybe that's the issue here. Possibly, some code is missing covering your case. Interestingly, macroexpand does not work on your qqq-macro either: user= (macroexpand '((qqq) (even? 42) boo!)) ((qqq) (even? 42) boo!) That's weird. Dominikus On 15 Mrz., 01:24, Ken Wesson kwess...@gmail.com wrote: On Mon, Mar 14, 2011 at 6:32 PM, Dominikus dominikus.herzb...@gmail.com wrote: I did some investigations on the code in Compiler.java. There is an IPersistentMap called 'specials' (line 95, Clojure 1.2) that maps symbols like 'if' to parsers like IfExpr.Parser(); obviously, these are the parsers recognizing special forms. Method analyzeSeq (line 5347) tries macro expansion first and gets the operator of a sequence form next. There is some special behavior if the operator is FN (namely 'fn*' for which no parser is associated in 'specials'; I don't know why 'fn*' gets special treatment here), then special parsers are activated if the operator is a special symbol, otherwise InvokeExpr.parse() is run. That doesn't quite seem right: user= (defmacro if [x y] `(println ~x ~y)) #'user/if user= (if (even? 42) boo!) boo! user= (user/if (even? 42) boo!) true boo! nil user= Clearly, the if special symbol, when in operator position, shadows even macros, forcing the use of a fully-qualified name to employ a macro named if. Perhaps you meant it does macroexpansion of the subforms first, but not (yet) of the whole form? Then it checks for the whole form to be a special form; then for the whole form to be a macro in need of expansion; and then treats it as a function call. If so, it's only the subforms not in operator position that get macroexpanded first. Otherwise user= (defmacro qqq [] 'if) #'user/qqq user= ((qqq) (even? 42) boo!) #CompilerException java.lang.Exception: Unable to resolve symbol: if in this context (NO_SOURCE_FILE:607) would instead produce boo!, as (qqq) would be expanded to if and then (if (even? 42) boo!) would be parsed and discovered to be an if special form. On the other hand, user= (quote (qqq)) (qqq) seems to indicate that nothing is macroexpanded before special form parsing occurs, or it would have output if instead of (qqq), unless quote it another special case. -- 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
Evaluation of Symbol Bindings vs. Special Forms
I'm not sure I fully understand symbol resolution and the evaluation strategy of symbol bindings and special forms in Clojure 1.2. Let's say I bind 'if' (usually being a special form) to a function: user= (defn if [x] (* x x)) #'user/if However, calling 'if' with an unqualified name does not work. Still, 'if' works like a special form: user= (if 3) java.lang.Exception: Too few arguments to if (NO_SOURCE_FILE:74) user= (if true 1 2) 1 However, 'if' itself evaluates to the new function on the REPL: user= if #user$if user$if@118d189 But I can call the redefined 'if' only with a qualified symbol name user= (user/if 3) 9 Why is that? I find it inconsistent to see 'if' evaluating to a function in the REPL but not in the context of a list; in a list form I'm forced to use a qualified name to suppress interpretation as a special form. What is the precise evaluation strategy? (I know, it's dangerous to redefine special forms. Here, I'm interested in the evaluation strategy of special forms vs. bound symbols.) Cheers, Dominikus -- 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: Evaluation of Symbol Bindings vs. Special Forms
This was also my best guess, Timothy and Stuart, but it does not explain why a redefined 'if' (or a competing symbol binding, Stuart?) in the REPL leads to user= if #user$if user$if@118d189 while 'if' within a list form does not: user= (if 3) java.lang.Exception: Too few arguments to if (NO_SOURCE_FILE:74) I think this is an inconsistent interaction model -- it might even hint towards an implementation flaw. If I manually decompose a list form and analyze its elements in isolation (first evaluate 'if' and then '3' in the REPL), the composite list form should behave accordingly, shouldn't it? I come up with this scenario in order to understand the precise behavior of Clojure's evaluation strategy regarding symbol bindings and special forms. As I said, I'm fully aware that it's no good idea to change 'if' or any other special forms in production code. Dominikus On 14 Mrz., 18:26, Stuart Sierra the.stuart.sie...@gmail.com wrote: Yes, with one correction: Clojure is not an interpreter. The Clojure compiler treats certain symbols specially, such as `if`, `do`, and `def`. You cannot redefine these symbols. -Stuart Sierra clojure.com -- 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: Evaluation of Symbol Bindings vs. Special Forms
I did some investigations on the code in Compiler.java. There is an IPersistentMap called 'specials' (line 95, Clojure 1.2) that maps symbols like 'if' to parsers like IfExpr.Parser(); obviously, these are the parsers recognizing special forms. Method analyzeSeq (line 5347) tries macro expansion first and gets the operator of a sequence form next. There is some special behavior if the operator is FN (namely 'fn*' for which no parser is associated in 'specials'; I don't know why 'fn*' gets special treatment here), then special parsers are activated if the operator is a special symbol, otherwise InvokeExpr.parse() is run. All this works as expected. Method analyze (line 5154) analyzes a given expression. If its a symbol, then analyzeSymbol (line 5556) is called, if its a form of kind ISeq, then analyzeSeq is called. There are more checks, of course. As one can see in analyzeSymbol, bindings are resolved but there is no check whether a special symbol like 'if' is used or not. The analysis of the code confirms the behavior of a redefined i.e. bound 'if' symbol. In favor of a consistent user experience (as described in the previous post), it simply shouldn't be possible to bind special symbols like 'if' to values at all. I guess that some few lines in Compiler.java could fix that. I don't know how Lisp treats this case. Dominikus On 14 Mrz., 22:09, Timothy Baldridge tbaldri...@gmail.com wrote: Yes, I think if you dig down into the Compiler.java code (that's where I assume this issue comes from) you'll probably find your answer. Recently in a blog entry Eric Lippert (one of the developers of C#) handled this exact subject. There are some 'bugs' in languages that exist, not because the developers don't know that they exist, but because actually reproducing the bug results in code that would never be used in production code. Sure this could be viewed as a bug, but who cares? Timothy I think this is an inconsistent interaction model -- it might even hint towards an implementation flaw. If I manually decompose a list form and analyze its elements in isolation (first evaluate 'if' and then '3' in the REPL), the composite list form should behave accordingly, shouldn't it? -- 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