2009-07-16
| 00:30 | Knekk | bleh, using the Emacs Starter Kit messes up my arrow keys |
| 00:44 | ataggart | is there a macro somewhere that will evaluate forms until one returns logical true? |
| 00:45 | ataggart | I seem to recall it, but can't find it |
| 00:49 | ataggart | aha! (some eval ['(foo ...) '(bar ...) '(baz ...)]) |
| 00:50 | skalnik | Learning Clojure has given me a headache :D |
| 00:50 | ataggart | thats the feeling of new pathways being burrowed through your brain. |
| 00:50 | skalnik | yep! |
| 00:51 | skalnik | I've given up on Project Euler problem #2 for now :) |
| 00:51 | ataggart | do you have a fib function? |
| 00:52 | skalnik | yep, that works :) |
| 00:53 | skalnik | I got stuck on trying to create a set of all the fib values under 4 million |
| 00:53 | ataggart | (reduce + (filter even? (take-while #(<= % 4e6) (fib-seq)))) |
| 00:53 | ataggart | booyah |
| 00:54 | skalnik | Haha |
| 00:54 | skalnik | Why reduce instead of apply? |
| 00:54 | skalnik | What's the difference |
| 00:54 | skalnik | (if any) |
| 00:55 | ataggart | lazy |
| 00:55 | skalnik | ah |
| 00:55 | ataggart | I think |
| 00:55 | ataggart | hmm |
| 00:55 | ataggart | no cant be about laziness |
| 00:56 | ataggart | since you have to run thru the whole list |
| 00:56 | ataggart | but "reduce" is more desciptive |
| 00:56 | skalnik | Now I just need to generate fib-seq |
| 00:56 | skalnik | I agree :) |
| 00:57 | ataggart | ah ok, apply would work but only because + can take any number of args |
| 00:58 | ataggart | reduce is when you have a function that takes two args |
| 00:58 | ataggart | and crepeatedly applies it |
| 00:58 | skalnik | ah ha |
| 00:58 | skalnik | So what I want to do for fib-seq is (map #(fib %) (range 10)), but replace 10 with a very large number |
| 00:59 | skalnik | However, that seems wasteful |
| 00:59 | ataggart | hmm not sure how you're trying to apprach it. doesn't sound right |
| 01:00 | skalnik | so I have a fib function, so if you call (fib 3) it gives the third fibonacci number |
| 01:00 | skalnik | The next step I wanted to do is create a set of fibonacci numbers less than 4 million |
| 01:00 | hiredman | someone should just write a canonical fib function so everyone can get on with their lives |
| 01:01 | ataggart | no one that needs to write one is doing for any reason than to learn. be gentle. |
| 01:02 | ataggart | skalnik, the thing is you don't want a function that takes an index |
| 01:02 | ataggart | you want a function that will return a sequence of all values |
| 01:03 | skalnik | I suppose |
| 01:03 | skalnik | I haven't learned that much though, and I'm quite tired, plus I have surgery tomorrow, so I should be heading to bed :) |
| 01:03 | skalnik | Thanks for the help ataggart |
| 01:04 | skalnik | also, my method works, just seems improper to me :) |
| 01:04 | ataggart | your intuition is correct ;) |
| 01:09 | skalnik | Very cool that no matter what large number I plug in, execution time is still roughly the same :) |
| 01:09 | skalnik | now, I really should be off to bed. |
| 03:08 | hiredman | clojurebot: CA |
| 03:08 | clojurebot | CA is Contributor Agreement: http://clojure.org/contributing |
| 03:27 | ataggart | mailed mine today |
| 05:27 | Jomyoot | (with-query-results rs ["select * from blogs"] |
| 05:27 | Jomyoot | Does that have lisp syntax for select statement? |
| 05:27 | Jomyoot | http://en.wikibooks.org/wiki/Clojure_Programming/Examples/JDBC_Examples shows lips syntax for other commmadns but not select |
| 05:55 | Jomyoot | How do I output Clojure's vector of int from Java? |
| 05:56 | Jomyoot | I am in Java. I want to output Vector of int, that is usable by Clojure |
| 05:56 | jdz_ | use iterators? |
| 05:56 | jdz | or you can convert a vector to array |
| 05:57 | Jomyoot | Clojure has [1 2 3 4] |
| 05:57 | Jomyoot | vector |
| 05:58 | Jomyoot | I want to output [1 2 3 4] from Java object. that will work as [1 2 3 4] in Clojure with no conversion |
| 05:58 | Jomyoot | Is that possible? |
| 05:58 | jdz | what do you mean by "output"? |
| 05:59 | Jomyoot | return |
| 05:59 | Jomyoot | return from Java method |
| 06:00 | jdz | Clojure vectors are Java objects (as any value in Clojure) |
| 06:00 | jdz | you can pass them and retourn to any Java methods you want |
| 06:00 | Jomyoot | hmm |
| 06:00 | jdz | I don't really understand what you want |
| 06:01 | Jomyoot | Vector<Integer> vect= new Vector<Integer>(100); return vect; |
| 06:01 | Jomyoot | How would Clojure see vect? |
| 06:02 | jdz | as an instance of Vector class |
| 06:02 | jdz | Java Vector class |
| 06:02 | Jomyoot | hmm |
| 06:03 | Jomyoot | What would I output from Java then If I want Clojure to see as native [1 2 3 4] |
| 06:03 | Jomyoot | Clojure's native |
| 06:03 | rottcodd | Jomyoot: I think you want to create a clojure.lang.PersistentVector |
| 06:03 | jdz | ,(class [1]) |
| 06:03 | clojurebot | clojure.lang.LazilyPersistentVector |
| 06:05 | Jomyoot | Is it a standard practive to create a LazilyPersistentVector in Java. then return to Clojure? |
| 06:05 | Jomyoot | As means of interop between java/clojure? |
| 06:06 | jdz | k |
| 06:06 | jdz | sorry |
| 06:06 | jdz | (that was C-x k in the wrong window) |
| 06:07 | jdz | Jomyoot: you can use all the classes and interfaces in src/jvm/clojure/lang from Java |
| 06:09 | Jomyoot | Arrays |
| 06:09 | Jomyoot | Clojure supports the creation, reading and modification of Java arrays. It is recommended that you limit use of arrays to interop with Java libraries that require them as arguments or use them as return values. |
| 06:09 | Jomyoot | Why ? what's wrong with using arrays? |
| 06:11 | jdz | they are not persistent |
| 06:11 | jdz | and there is nothing wrong with them |
| 06:11 | jdz | you just have to be very careful if you are using them |
| 06:11 | jdz | that's why the rule of thumb of using them only for interop holds |
| 06:13 | Jomyoot | Is there easy ways to quickly convert array of int or vector of int to Clojure's native data? |
| 06:16 | rottcodd | vec |
| 06:16 | cark | or seq |
| 06:17 | Jomyoot | seq |
| 06:17 | Jomyoot | i want to make seq out of java array of vector |
| 06:17 | Jomyoot | possible? |
| 06:17 | Jomyoot | must be done manually? |
| 06:18 | Jomyoot | or is there built in method |
| 06:18 | cark | just use the seq function on your array |
| 06:19 | Jomyoot | seq also works on Strings, native Java arrays (of reference types) |
| 06:19 | Jomyoot | what is reference types? |
| 06:19 | Jomyoot | is int = reference types? |
| 06:19 | jdz | ,(map (partial + 1) (seq (into-array [1 2 3 4 5]))) |
| 06:19 | clojurebot | (2 3 4 5 6) |
| 06:20 | cark | i think in this context reference type means objects as opposed to primitive values |
| 06:20 | Jomyoot | hence array of integer will not work |
| 06:20 | jdz | i just showed you that it works |
| 06:21 | cark | ,(seq (make-array Integer/TYPE 10)) |
| 06:21 | clojurebot | (0 0 0 0 0 0 0 0 0 0) |
| 06:21 | jdz | in the example above a native Java array is created from Clojure vector, and then back to a sequence |
| 06:21 | Jomyoot | is 1 2 3 4 in Clojure represented as int or Integer in Java? |
| 06:21 | cark | it depends |
| 06:22 | cark | these are boxed before doing function calls, but could be native integers inside a function |
| 06:22 | cark | also you can use native integers in java array |
| 06:23 | cark | so there would not be any boxing on function calls i guess |
| 06:23 | ChrisPS | ,#'in-ns |
| 06:23 | clojurebot | #'clojure.core/in-ns |
| 06:30 | cark | Jomyoot : native integer arrays do work with seq ...but the seq will contain boxed integer in the end |
| 06:30 | cark | ,[(into-array [1 2 3 4 5]) (make-array Integer/TYPE 10)] |
| 06:30 | clojurebot | [#<Integer[] [Ljava.lang.Integer;@1b206f> #<int[] [I@ee90e9>] |
| 06:31 | Jomyoot | well then |
| 06:31 | cark | as you can see the second form creates an array of native ints |
| 06:32 | Jomyoot | does it matter in clojure wheter it's native or boxed int? |
| 06:32 | cark | no more than it matters to java |
| 06:32 | Jomyoot | do clojure functions that require ints care? |
| 06:32 | Jomyoot | well then it does matter then |
| 06:32 | cark | the only difference to my knowledge is performances |
| 06:32 | Jomyoot | some methods expect boxed some expect native |
| 06:33 | Jomyoot | i would assume that most clojure functions expect either native or boxed but not both |
| 06:33 | cark | have you an example of a java function that expects native ? |
| 06:33 | cark | i'm not knowledgable at all on the java side of things, looks like something interesting to test |
| 06:34 | jdz | many methods that accept characters also accept ints |
| 06:34 | Jomyoot | more methods expect int[] than Integer[] |
| 06:34 | jdz | like, Character/isDigit or something |
| 06:34 | Jomyoot | more methods expect int than Integer as well |
| 06:34 | Jomyoot | and all number literals in Java are native |
| 06:34 | Jomyoot | not boxed |
| 06:35 | cark | ,(Character/isDigit (new Integer 3)) |
| 06:35 | clojurebot | false |
| 06:35 | cark | that's a boxed integer i'm passing there |
| 06:36 | cark | it is my beleif that the vm takes care of the conversion |
| 06:36 | Jomyoot | ,(Character/isDigit 1) |
| 06:36 | clojurebot | false |
| 06:37 | jdz | ,(Character/isDigit (int \5)) |
| 06:37 | clojurebot | true |
| 06:37 | cark | though i don't think that applies to arrays |
| 06:37 | keca | ,(Character/isDigit (int \5)) |
| 06:37 | clojurebot | true |
| 06:37 | cark | ,(Character/isDigit (Integer. (int \5))) |
| 06:37 | clojurebot | true |
| 06:37 | grrrt | ,(int \5) |
| 06:37 | clojurebot | 53 |
| 06:37 | jdz | 5 is *the* number :) |
| 06:37 | keca | ,\5 |
| 06:37 | clojurebot | \5 |
| 06:39 | grrrt | isDigit has two implementations: one with a char and one with an int |
| 06:39 | grrrt | the int version assumes the int you pass in is a unicode codepoint |
| 06:39 | cark | anyways the easy way to get an int[] array from a vector is this : (into-array Integer/TYPE [1 2 3 4 5]) |
| 06:43 | mikem` | Hi, i'm trying to use a class from a jar file at the REPL in vimclojure, but I keep getting FileNotFoundException. I'm new to clojure and Java, can someone help me get started? |
| 06:44 | grrrt | if you get a FileNotFoundException, you're trying to load the class as a file? |
| 06:45 | grrrt | it should be on your java classpath, then you can use 'import' to use it in your namespace |
| 06:45 | cark | you need to start java with the path to your jar added to the classpath parameter |
| 06:46 | mikem` | grrrt: I'm trying to load HttpPost from org.apache.http.client.methods (for example). I try (use 'org.apache.http.client) and get the FileNotFoundException |
| 06:46 | mikem` | ah, import... ok, let me give that a shot |
| 06:46 | grrrt | yeah use is for clojure namespaces |
| 06:48 | mikem` | grrrt: ok so I need to specify the .jar file on the classpath when I execute the nailgun server (for vimclojure). do I have to specify every .jar file or can I specify a directory with .jar files? the relevant jar files are in my pwd |
| 06:49 | grrrt | every jar file |
| 06:49 | cark | you can use wildcards for jvm 1.6 |
| 06:49 | grrrt | ah yes, that's true |
| 06:49 | grrrt | heh. spent too long on old jvms :( |
| 06:50 | cark | here is how i start java on my current project : java -server -Xmx300m -XX:+UseParallelGC -cp c:/home/cara/clojure/WebTL/lib/*;c:/home/cara/clojure/WebTL/src/ clojure.main |
| 06:50 | mikem` | grrrt: ok, that's what I needed: use import and specify all jars on the command line. thanks! |
| 06:50 | grrrt | no worries, have fun! |
| 06:50 | cark | note that there is only one star |
| 06:50 | mikem` | cark: the wildcard doesn't seem to work for me |
| 06:51 | cark | what's your jvm version ? |
| 06:51 | grrrt | mikem`: if you do 'java -version', what do you get? |
| 06:51 | mikem` | 1.6 |
| 06:51 | grrrt | hm |
| 06:51 | cark | i swear it works =P |
| 06:51 | mikem` | 1.6.0_14 |
| 06:51 | mikem` | cark: I'm probably doing it wrong... |
| 06:51 | cark | i have like 8 libraries in there in use for this project |
| 06:52 | grrrt | you'd normally start your stuff from a shell script, ant file or maven file anyways |
| 06:52 | grrrt | you wouldn't use a plain "java ............ clojure.main" command every time |
| 06:52 | cark | ahwell the end product is an executable jar |
| 06:53 | cark | so there's no scrip, and no customer mess up =) |
| 06:53 | grrrt | heh |
| 06:54 | mikem` | cark: ok, with this command, everything works: http://dpaste.com/67796/ |
| 06:55 | cark | great then =) |
| 06:55 | cark | though it might be worth working out the wildcard thing to keep things simple |
| 06:57 | mikem` | ok, will try that after some food. thanks for the help so far |
| 07:03 | grrrt | hmm there was a trick to using names that were defined as private, but what was it? I'm trying to access a def that is private from a test-is testcase (in a different file) |
| 07:07 | grrrt | Ah, got it. to answer my own question: http://groups.google.com/group/clojure/browse_thread/thread/3835e5405ab930f6/ |
| 07:50 | Jomyoot | Can file a.clj (:use b) and b.clj (;use a)? |
| 07:51 | cark | nope |
| 07:52 | cark | that's bad practice anyways |
| 07:52 | Jomyoot | seriously |
| 07:53 | Jomyoot | hmmm |
| 07:55 | cark | what you could do is this : load a, load b, both loaded from c ... c tells a where to find b's functions it needs, c tells b where to find a's functions it needs |
| 07:57 | cark | for instance in a you would have (def my-proxy-to-b-hello) and from c you could (set! my-proxy-to-b-hello hello) |
| 07:57 | cark | (untested) |
| 08:01 | cark | or better yet, make A use B, then when A calls a function in B use a function parameter for the call B needs to do in A. you could use a map of functions if you want to pass several functions |
| 08:22 | maacl | Using Emacs/Slime how do I identify the Clojure source line/function that resulted in a specific runtime exception? The traceback only seems to provide the Clojure .java file that failed (in this case the LazySeq.java) |
| 08:23 | durka42 | try clojure.stacktrace/print-cause-trace |
| 08:23 | dysinger | I noticed clojure master on github has 'backtrace' too |
| 08:23 | dysinger | (not 1.0) |
| 08:26 | maacl | That gives me java.lang.ClassNotFoundException: clojure.stacktrace (NO_SOURCE_FILE:0) |
| 08:33 | maacl | durka42: Should I do "clojure.stacktrace/print-cause-trace" at the Repl? |
| 08:34 | durka42 | do (use 'clojure.stacktrace) first |
| 08:34 | clojurebot | clojure is like life: you make trade-offs |
| 08:34 | durka42 | unless you don't have the latest clojure, in which case it might still be in clojure.contrib.stacktrace |
| 08:36 | maacl | durka42: ah, was in contrib |
| 08:38 | maacl | durka42: clojure.contrib.stacktrace/print-cause-trace -> #<stacktrace$print_cause_trace__4400 clojure.contrib.stacktrace$print_cause_trace__4400@5be2753e> |
| 08:38 | Chouser | whoa. I hadn't noticed that stacktrace came in with gtic |
| 08:38 | durka42 | apparently |
| 08:39 | Chouser | that would explain that repl startup error I keep seeing but hadn't investigated yet... |
| 08:39 | durka42 | :p |
| 08:40 | maacl | durka42: How is it supposed to be used ? |
| 08:41 | durka42 | well, you cause an exception and then you can do (print-cause-trace *e) |
| 08:50 | maacl | durka42: does this have to be from the repl or can I do it from within a source file? |
| 08:52 | maacl | durka42: from within the source file it gives a nullpointer exception |
| 08:57 | durka42 | you could catch the exception and run print-cause-trace on it |
| 09:11 | Jomyoot | i know there isn't for loop |
| 09:11 | Jomyoot | but how would i efficiently emulate for loop. where i loop over 0..n |
| 09:12 | Jomyoot | omg there is (loop) |
| 09:12 | Chouser | the key question is why are you looping: producing side-effects? producing and returning a lazy seq? producing and returning some other kind of collection? |
| 09:13 | Chouser | ,(for [i (range 10)] (+ i i)) |
| 09:13 | clojurebot | (0 2 4 6 8 10 12 14 16 18) |
| 09:13 | Jomyoot | producing side effect |
| 09:13 | Jomyoot | inserting into database |
| 09:13 | ChrisPS | ,(repeat 7) |
| 09:13 | clojurebot | Eval-in-box threw an exception:java.lang.OutOfMemoryError: Java heap space |
| 09:13 | Jomyoot | where I need to change index value from 0 to n |
| 09:13 | ChrisPS | ok |
| 09:13 | Chouser | ok, so you probably want doseq |
| 09:14 | Chouser | (doseq [i (range 10)] (insert-into-db i)) |
| 09:14 | Chouser | that'll return nil after callking insert-into-db 10 times |
| 09:15 | Chouser | actually, dotimes is even better |
| 09:15 | Chouser | (dotimes [i 3] (prn i)) |
| 09:15 | Chouser | ,(dotimes [i 3] (prn i)) |
| 09:15 | clojurebot | 0 1 2 |
| 09:17 | Jomyoot | ,(range 20) |
| 09:17 | clojurebot | (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19) |
| 09:18 | Jomyoot | is there sin for using (for)? |
| 09:19 | Chouser | Jomyoot: nope, 'for' is good. But it's lazy, which means if you're producing side-effects you can't be sure when (or if) they will ever actually happen. |
| 09:20 | Jomyoot | i have to read into laziness |
| 09:24 | Chouser | ,(do (for [x [1 2 3]] (prn x)) "all done?") |
| 09:24 | clojurebot | "all done?" |
| 09:25 | Chouser | note the numbers never got printed. So it's best not to use 'for' if you want side effects. Instead use doseq (or dotimes, or loop/recur, etc.) |
| 09:25 | Chouser | ,(do (doseq [x [1 2 3]] (prn x)) "all done?") |
| 09:25 | clojurebot | "all done?" |
| 09:25 | clojurebot | 1 2 3 |
| 09:25 | jdz | ,(do (prn "foo") "done") |
| 09:25 | clojurebot | "done" |
| 09:25 | clojurebot | "foo" |
| 09:25 | Chouser | heh |
| 09:27 | jdz | clojurebot should prefix the lines depending on what kind of output it is (standard output/error, return value) |
| 09:27 | jdz | i'm not volunteering ;0 |
| 09:28 | jdz | although i imagine the change would not be complicated |
| 09:59 | artagnon | Different CL implementations on different platforms have different strengths and weaknesses. Why tie Clojure down to an implementation? |
| 10:00 | Chousuke | What do you mean? |
| 10:00 | jdz | artagnon: there's clojure-clr, did you know? |
| 10:00 | clojurebot | "[Clojure ...] feels like a general-purpose language beamed back from the near future." |
| 10:00 | artagnon | There are tons of Scheme implmentations too. Gambit compiles directly to C and can be really really quick. PLT is the most widely used flavor of Scheme- it's excellent for beginners. |
| 10:00 | jdz | artagnon: anyway, what's your point? |
| 10:01 | jdz | artagnon: considering that Clojure is mainly a one-man project, and very young? |
| 10:01 | Chousuke | artagnon: The "official" clojure is "tied" to the JVM platform because that gives a multitude of benefits. |
| 10:02 | artagnon | jdz: I don't have a point. I'm asking the reason for the design decision |
| 10:02 | Chousuke | artagnon: nothing is going to stop you from implementing a flavour of Clojure for other host platforms though, but they will likely be incompatible, at least if host interop is used. |
| 10:02 | Chousuke | ~rationale |
| 10:02 | clojurebot | rationale is http://clojure.org/rationale |
| 10:02 | Chouser | Early Clojure supported both JVM and CLR |
| 10:03 | Chousuke | I suppose perhaps in the future there might be a Clojure specification, if someone bothers to write one. |
| 10:03 | Chousuke | but I think being implementation-defined allows Clojure to progress faster, which is important. |
| 10:04 | Chouser | I think the two main factors that caused the CLR support to be dropped were (a) the design of Clojure is not complete, and (b) one mature implementation is better than 2 immature implementations |
| 10:04 | artagnon | hm. So there's no specification draft? Just a couple of implementations to date. |
| 10:04 | artagnon | Chouser: I see. |
| 10:04 | Chousuke | artagnon: yes. The JVM Clojure implementation defines Clojure. |
| 10:05 | Chouser | there is one implementation, and it's structure and design is still evolving, though several of the key core concepts are quite stable and unlikely to change. |
| 10:05 | Chousuke | There are also plans to rewrite the Clojure compiler in Clojure, which ought to make Clojure more "portable" |
| 10:05 | Chouser | then there are a couple incomplete ports trying to follow along |
| 10:06 | cark | at the end of the day, the one implementation does the job, and we finally get to use a lisp in real life |
| 10:06 | artagnon | What's the current Clojure currently written in? Java? |
| 10:06 | Chouser | artagnon: yes, mostly Java plus some Clojure |
| 10:06 | Chouser | artagnon: http://blog.n01se.net/?p=41 |
| 10:07 | Chousuke | In addition to that, the core libraries use some java interop |
| 10:07 | cemerick | I've never quite understood the desire to have a specification -- seems like that's a great way to cast a language in amber, but exactly what you don't want to foster progress/innovation *shrug* |
| 10:08 | artagnon | Clojure-in-Clojure is fine, but you need a Lisp environment first to eval those sexps. |
| 10:08 | Chouser | cemerick: I agree. |
| 10:08 | Chouser | artagnon: yep! And we have one that's nearly good enough |
| 10:08 | artagnon | Chouser: we do? |
| 10:08 | cark | artagnon : you only need those primitive forms |
| 10:09 | artagnon | cark: ofcourse. |
| 10:09 | artagnon | to be able to understand the Clojure that defines the rest of Clojure. |
| 10:09 | cark | as opposed to all the data structures and "standard library" |
| 10:09 | artagnon | Like the inbuilts in Elisp (written in C) |
| 10:09 | Chouser | In a closed-source world, a language spec is fine -- necessary, I suppose. But I'm entirely content to have a single open-source implementation instead. |
| 10:10 | Chouser | artagnon: Clojure as it stands is nearly good enough to write Clojure-in-clojure well. |
| 10:10 | Raynes | What would be the best way to make a sequence of the result of 5 read-line calls without typing (read-line) 5 times? |
| 10:10 | jdz | instead of writing language specification i'd rather like to see conformance tests being written |
| 10:10 | artagnon | Raynes: write a macro that unfolds the read-line 5 times? |
| 10:10 | Chouser | Raynes: (take 5 (repeatedly #(read-line x))) |
| 10:11 | Chousuke | beware: lazy seq :) |
| 10:11 | Chouser | Raynes: (vec (take 5 (repeatedly #(read-line x)))) |
| 10:11 | Chouser | :-) |
| 10:11 | Raynes | Thanks. :D |
| 10:12 | artagnon | Do equivalent Java programs perform worse? ie. I'm asking if we have an efficient compiler (the Clojure bytecode compiler ie.) |
| 10:13 | Chouser | the byte-code produced by Clojure is efficient |
| 10:13 | cark | well to be honest clojure is slower |
| 10:14 | Chousuke | Clojure is typically slower than java, if you pay no attention your tight loops |
| 10:14 | artagnon | I see. |
| 10:14 | cark | boxing of primitive values, and function call semantics |
| 10:14 | Chouser | but that doesn't have to do with the bytecode produced by the compiler |
| 10:14 | cark | but inside a function you can get as fast as java |
| 10:14 | Chousuke | I mean, (reduce + (range 100000)) will not beat the equivalent java loop, but it's a lot easier to write :P |
| 10:14 | artagnon | Chouser: Then what does it have to do with? |
| 10:15 | artagnon | Chousuke: Why? What's the reason behind this? |
| 10:15 | jdz | Chousuke: and better yet, (reduce + (range 1000000000000)) |
| 10:15 | Chousuke | artagnon: well, + is a function. |
| 10:15 | cark | artagnon : and range produces a lazy sequence |
| 10:15 | cark | that's overhead for convenience tradeoff |
| 10:16 | artagnon | Ah, I see. |
| 10:16 | cark | still you have the choice to make it as fast as java, by doing the same ugly thing as java |
| 10:16 | rhickey | Clojure calls to methods are precisely the same bytecode as Java's, so there's no general truth to 'Clojure is slower than Java'. Clojure's primitive numerics are also as fast as Java's. But, Clojure uses (Java's) boxed numbers by default and (Java's) boxed numbers are slower than (Java's) primitives. |
| 10:17 | Chouser | the biggest common speed hit is when the code provides insufficient type hints such that reflection is done at runtime. This is very convenient for the programmer, but huge slowdown |
| 10:17 | scgilardi | along the lines of readline before, this works for me: (take 5 (repeatedly read-line)) (I don't see read-line taking an arg) |
| 10:17 | cark | rhickey : well calling a clojure function from clojure is slower than java, and primitives are always boxed on function boundaries |
| 10:17 | rhickey | cark: actually fewer ugly things, as CLojure infers much more given fewer hints |
| 10:17 | cark | agreed |
| 10:18 | cark | hey i'm sold on clojure ! just trying to be honest =) |
| 10:18 | artagnon | Another thing I'd like to adress here: Many people hate Java. One of the reasons is that it's this whole shebang dump of this isolated box: Java has its own database (some JDBC or some such rubbish), graphical toolkit (Swig, right?) and other things. |
| 10:19 | Chousuke | artagnon: JDBC is just an API for accessing databases, not a database in itself. And the graphical toolkit is Swing. But there are also Qt bindings for java if you'd like to use that instead :) |
| 10:19 | artagnon | Chouser: Right, I suppose there are some optimization features that trade off convinience for speed to use in specific bottlenecks in Clojure. |
| 10:19 | rhickey | well, it's the generalities that are the problem. We can talk about anything specific, but people read those general statements and learn nothing about the truth of how to make things fast and what are the bottleneck. For instance, many have concluded that calling is slowing when it was primitive arg boxing holding things up |
| 10:20 | rhickey | So tight-loop benchmarks that pass primitives as args suffer, but many others don't at all |
| 10:20 | artagnon | rhickey: so the slowdown has to do with the language's design (relying on boxed types for example) and not the design of the bytecode compiler itself. |
| 10:21 | artagnon | Chousuke: Frankly, I was unaware. Sorry, I never really bothered to learn any Java. |
| 10:21 | rhickey | artagnon: the JVM bytecode is so simple, and most optimizations are actually performed by HotSpot, so Clojure gets them |
| 10:21 | rhickey | free |
| 10:21 | artagnon | ah, I see. |
| 10:22 | rhickey | It is a JVM limitation that one can't write fixnums as would be common in a dynamic lang |
| 10:22 | Chousuke | artagnon: well, I haven't bothered much with Java either. But Clojure is fun, and it helps to know the java libraries if you work with Clojure :) |
| 10:22 | rhickey | not a language design issue - if there were unboxed fixnums in the JVM Clojure would use them |
| 10:23 | artagnon | Chousuke: Right :) |
| 10:23 | rhickey | The JVM won't let a single type hold either a reference or a non-heap entity |
| 10:23 | Chouser | artagnon: the Java insular universe is actually related to my biggest annoyances with Clojure. But then again, the engineering work that has gone into that Java universe (JVM performance, library availability, etc.) is one of the things I like the most. |
| 10:23 | Chousuke | Is it possible to add fixnum support to the JVM without breaking backwards compatibility? :/ |
| 10:23 | artagnon | rhickey: So it's more about the JVM not being tailor-made for Clojure. |
| 10:24 | rhickey | artagnon: fixnums could be used by any dynamic lang on the JVM |
| 10:24 | rhickey | http://blogs.sun.com/jrose/entry/fixnums_in_the_vm |
| 10:24 | cark | i think there's a push for better dynamic language support on the jvm ... we might see some progress |
| 10:24 | artagnon | Chousuke: Hey, nobody has actually explained this to me: But why is Java so isolated from the rest of the world? |
| 10:24 | Chousuke | artagnon: fortunately, there seems to be a lot of work going into making the JVM better for dynamic languages lately. :) |
| 10:25 | artagnon | rhickey: anything else apart from the fixnums that would help? |
| 10:25 | Chousuke | artagnon: Hmm. I don't think it's any more isolated than other languages utilising virtual machines. |
| 10:25 | rhickey | artagnon: tail call optimization |
| 10:25 | cark | artagnon : not any more isolated than CL |
| 10:25 | artagnon | rhickey: It doesn't have tail call optimization!? |
| 10:25 | rhickey | artagnon: nope |
| 10:26 | artagnon | cark: I thought CL was isolated because it's difficult and generally unpopular, no other reason :p |
| 10:26 | Chousuke | I actually prefer recur over true tail calls. it throws an exception if I get it wrong :P |
| 10:26 | artagnon | hm, that's terrible. SBCL has nice tail call optimization. |
| 10:26 | artagnon | Chousuke: ofcourse, that's for testing. |
| 10:27 | artagnon | In production, I'd let SBCL optimize it. |
| 10:27 | Chousuke | artagnon: no, I mean the recur special form in clojure, which is a workaround for the lack of TCO :) |
| 10:27 | artagnon | er, sorry. I'm unaware of this form. |
| 10:27 | rhickey | artagnon: that's not the same at all - real TCO is a promise to eliminate, not an optional optimization. Only with the former can you design programs using calls as control flow without worrying about the stack |
| 10:28 | cark | lack of TCO kept me away from clojure for a long time, than i tried it anyways, and i'm very glad i did =) |
| 10:28 | rhickey | I'm talking about Scheme-style TCO |
| 10:29 | Chousuke | artagnon: basically, if you have a tail-recursive function you'd call like (foo x y), instead of having (foo x y) in the tail position in the definition, you just have (recur x y) |
| 10:29 | cark | we have recur and trampolines to make it up, so that's really a non issue |
| 10:29 | Chousuke | artagnon: it can't do mutual recursion though. For that, you need a trampoline :/ |
| 10:29 | artagnon | Chousuke: oh I see! |
| 10:29 | artagnon | that's interesting though. |
| 10:30 | cark | artagnon : are you thinking about giving clojure a try ? |
| 10:30 | Chousuke | it's not as elegant as it would be if the JVM promised TCO, but it's not too bad. |
| 10:30 | Chouser | cark: and lazy seqs |
| 10:30 | artagnon | cark: ofcourse! |
| 10:30 | cark | chouser : ah yes indeed |
| 10:31 | artagnon | I'm just downloading it and setting up Emacs :) |
| 10:31 | Chousuke | artagnon: clojure-mode can do that for you, with clojure-install |
| 10:31 | cark | artagnon : great =) you'll find plenty of help in this channel to get you started (i abused it quite a bunch) |
| 10:31 | Chousuke | artagnon: also, http://java.ociweb.com/mark/clojure/ |
| 10:32 | artagnon | Chousuke: clojure-install doesn't work anymore :( |
| 10:32 | Chousuke | oh? :( |
| 10:32 | artagnon | Chousuke: the git repo mentioned in the code is gone |
| 10:32 | artagnon | I'll tell you which line, wait |
| 10:32 | Chousuke | hmm |
| 10:32 | Chousuke | maybe you have some old version of clojure-mode? |
| 10:33 | artagnon | I got it from jochu's GitHub head. |
| 10:33 | artagnon | and besides, I don't like auto-things anyway. Especially not auto-installing things. They freak me out a bit :p |
| 10:33 | Chousuke | hmm, yeah, that seems to be a bit outdated |
| 10:34 | Chousuke | http://github.com/technomancy/clojure-mode/tree/master this fork of it is a bit better maintained :) |
| 10:34 | artagnon | I want to be able to switch swank backends. I'm not going to stop writing CL or anything :p |
| 10:34 | Chousuke | heh |
| 10:34 | artagnon | Chousuke: ah, thanks! :) |
| 10:38 | Chousuke | rhickey: how's the chunks branch coming along, btw? |
| 10:39 | rhickey | Chousuke: I'd like to see some contribs for chunked for/take/drop etc |
| 10:39 | cemerick | heh, and I was going to ask about new-new. :-P |
| 10:41 | Chouser | c-in-c could be written today: reader and compiler are blocking on nothing and the data structures could use proxy. |
| 10:41 | rhickey | cemerick: newnew touches deeply on what I want Clojure to be. I'd be pretty happy to have it not have most of genclass, and have people always have to define interfaces |
| 10:41 | rhickey | I really don't want to embed Java in Clojure |
| 10:41 | Chouser | this would create a slower-clojure-in-clojure that may be a more pleasant language in which to write new-new? |
| 10:43 | rhickey | Chouser: not sure about that question - it would be as fast as Java, but not support some things, e.g. mutable fields |
| 10:43 | cemerick | rhickey: that's 95% of what I want, and the other 5% I can proxy. |
| 10:44 | Chouser | cemerick: when would you use proxy? |
| 10:44 | rhickey | cemerick: proxy with proxy? |
| 10:44 | rhickey | or did you mean stub somehow? |
| 10:44 | cemerick | There are times when one needs to produce a subclass, etc. |
| 10:45 | cemerick | rhickey: Maybe new-new would support that as well, but there's no telling, esp. the way you talk about interfaces. |
| 10:45 | rhickey | cemerick: so, subclasses are a key consideration right now. In fresh designs, I'm not in favor, but 2 things mitigate: many Java libs require concrete derivation, and: Clojure's impl makes use of abstract classes |
| 10:46 | cemerick | I remember your first shot at proxy (maybe a year ago, named differently) only worked with interfaces. |
| 10:46 | rhickey | It's about defining names classes more than deriving |
| 10:47 | cemerick | I think not being able to subclass would be a significant blow to interop and adoption. proxy would have to stick around in some form. |
| 10:47 | rhickey | so if you can derive from and abstract class, but not create one... |
| 10:48 | rhickey | I'm really concentrating on the cinc story - how to move the data structures over, what to do with the abstract bases - define a better way to do reuse-via-composition? |
| 10:49 | rhickey | or, a genclass variant with the same direct-call code gen as newnew? |
| 10:50 | artagnon | Is there a plan to port the Clojure compiler to say, LLVM? Would it serve any purpose? |
| 10:50 | cemerick | well, it seems that avoiding the indirection is critical for performance, so I'd assume anything genclass-esque is out |
| 10:50 | rhickey | newnew really needs fn + proxy, but half that code is in Java and the other half is in Clojure |
| 10:51 | rhickey | cemerick: there's no reason there couldn't be a genclass variant that had the method bodies inline |
| 10:51 | cemerick | well, in that case :-) |
| 10:52 | rhickey | artagnon: where are the libraries for LLVM? |
| 10:53 | artagnon | Oh right. Not only would you have to re-invent things like GC, but all the Java libraries that you took for granted too ~! |
| 10:54 | Chouser | rhickey: so it would be actually beneficial to have today's compiler feature set implemented in Clojure? Or am I reading too much into that. |
| 10:55 | cemerick | rhickey: couldn't an abstract base instance just be another field in the subclass object? supermethods would be directly callable... |
| 10:55 | artagnon | It took some time for me to grasp the fact that the JVM is so generic that it should be called HLVM instead. |
| 10:56 | Chouser | artagnon: or maybe OOVM |
| 10:56 | rhickey | Chouser: I think you may be reading into that, I'm still thinking about what it needs to do, not how to do it, other than it looking like a lot of work, either way it is done |
| 10:56 | artagnon | Chouser: it's object-oriented? |
| 10:56 | artagnon | what does OO have to do with it? |
| 10:58 | Chouser | artagnon: yes, as far as I know object, methods, inheritence, etc. are baked pretty deeply into both the JVM bytecode spec and the implementation(s). |
| 10:58 | rhickey | cemerick: It's about this - what's the recipe for writing Clojure in Clojure, especially the data structures? Does one rewrite the interfaces using gen-interface and write the impls using newnew? That leaves a hole around the abstract classes |
| 10:58 | artagnon | I see. I haven't looked into that at all actually. |
| 10:59 | rhickey | there are also a very few cases where even one other object creation impacts perf in a way that matters |
| 11:00 | rhickey | any composition strategy has issues there |
| 11:00 | artagnon | Another (probably very stupid) question: What kind of an environment does say, SBCL provide? It's very Lisp-specific and doesn't have the unnecessary features of the JVM. Does it lack something? |
| 11:02 | rhickey | Chouser: I have looked at the compiler stuff you've started - I guess I'm loathe to do a rewrite and not improve things. As I've said before, I wouldn't use dynamic binding similarly again, would pass environments explicitly, also the reflection is woven throughout and the use of class instances might be better with names |
| 11:04 | Chouser | yeah, the code I have tries to improve things it shouldn't ("instance" instead of fn) and fails to improve things it should (set! side effects all over the place) |
| 11:04 | rhickey | artagnon: any CL has only a tiny fraction of the libraries of Java, and also a tiny fraction of the users of those libraries, thus less testing etc |
| 11:05 | cemerick | rhickey: I guess I'd say that one should be able to produce abstract classes with newnew as well (though calling them that is a little odd -- they'd just be usages of newnew that don't have impls for all methods). The subclasses can refer to them by var-name, which (at least in my head) would allow you to pick up what the actual generated base classname is at runtime, and declare that base class as the superclass. |
| 11:05 | rhickey | Chouser: I understand the strategy of 'copy the Java into Clojure' in order to move forward in CLojure, might mean writing the compiler twice |
| 11:05 | cemerick | Doing anything else would seem to require developing a parallel object system or some other approach. |
| 11:05 | scgilardi | artagnon: and amazing toos for profiling, visibility into the workings of the VM |
| 11:06 | scgilardi | tools |
| 11:06 | rhickey | cemerick: that kind of dynamic use of class names is a definite no go. Java class names are static and I'm not going to pretend anymore, it's a disaster for OSGi and other modularity systems |
| 11:07 | Chouser | rhickey: that was the only strategy I could follow without understanding the inner workings of the current compiler better. except for the poorly-chosen attept to implement "instance". |
| 11:07 | artagnon | rhickey: Right. Got it. |
| 11:07 | danlarkin | artagnon: SBCL doesn't use a VM (as far as I know, at least). It does native code generation |
| 11:08 | cemerick | rhickey: Sounds fine by me. In that case, (new my.class.name [bases] [s-args] ...)? |
| 11:08 | rhickey | cemerick: in line with that, I think we need a distinct static phase of compilation, for interface defs at least |
| 11:08 | rhickey | cemerick: my.class.name is defined where? |
| 11:08 | artagnon | danlarkin: at all? Are you sure? I never thought stuff like hotpatching would be possible without a VM. |
| 11:09 | cemerick | rhickey: that's a symbol that controls the name of the class generated by the compiler when it loads the newnew form |
| 11:09 | rhickey | cemerick: for use by what other code? |
| 11:10 | rhickey | that's the fundamental problem, you can't dynamically define named JAva classes without a ton of problems, with classloaders, redefinition etc |
| 11:10 | cemerick | in the context of cinc, other newnew usages that define concrete impls (new c.l.SomePersistentDS [my.class.name other-interface] ...) |
| 11:11 | Chouser | counld abstract base classes be replaced by a sort of mix-in idea? new-new could generate classes that provide implementation from some other place when not specified in the form. |
| 11:11 | cemerick | that's mostly what I'm getting at, without having the benefit of intimate knowledge of the bytecode involved :-) |
| 11:11 | Chouser | would be a pain for Java code trying to re-use the abstract "class", but who wants to write java? |
| 11:12 | rhickey | Chouser: yes, exactly, that's the kind of thing I'm thinking about |
| 11:12 | artagnon | Thanks for everything, all. I'll give Clojure a spin. |
| 11:13 | rhickey | Chouser, cemerick: if not composition, then we're talking about some sort of mixin/macro thing |
| 11:13 | clojurebot | Who?? |
| 11:14 | rhickey | might be as simple as - use these defs if no others are provided |
| 11:14 | cemerick | that makes super somewhat more difficult, no? |
| 11:14 | rhickey | I could probably have done most of the data structures that way |
| 11:15 | cemerick | rhickey: this is where my lack of understanding of the details kills me. If there's only one "mixin", isn't that just an abstract class? |
| 11:15 | rhickey | cemerick: super wasn't that important for me, it's almost always a bad sign |
| 11:16 | cemerick | rhickey: critical (super-critical!) for interop, though. If you're going to care about things like osgi, then you've got to have an abstract class extension story. |
| 11:17 | rhickey | there are only 5 super uses (that aren;t ctor calls) in clojure.lang, 3 are forced on my by Java bases classes |
| 11:19 | rhickey | cemerick: we're are talking about replacing the abstract bases in clojure.lang, that's a separate question from being able to derive from abstract bases. Even if the latter is possible, doesn't mean we want to have to define abstract bases in clojure |
| 11:19 | rhickey | consuming vs producing |
| 11:20 | rhickey | in all likelihood newnew will retain proxy's ability to derive from concrete classes, and improve the super access story |
| 11:20 | rhickey | but that's not the same as providing tools for defining abstract classes (other than existing genclass) |
| 11:21 | rhickey | i.e. if newnew is the only way to define the highest-performing classes, how do you replace abstract bases in your Clojure designs? |
| 11:21 | rhickey | given newnew defines an anonymous class |
| 11:22 | cemerick | rhickey: well, see, I underestimated your ambitions again :-) |
| 11:24 | cemerick | rhickey: FWIW, I think traits are the best thing about Scala. Clojure could do worse than be influenced by them. |
| 11:24 | rhickey | a concrete code reuse mechanism other than inheritance is a promising idea, gets out of naming and dynamic redefinition troubles |
| 11:24 | Chouser | you could still do super with mixins if you want to |
| 11:25 | Raynes | Traits are nice. |
| 11:25 | cemerick | obviously a lot of "stuff" there related to the type system, but the general model and workflow is nice. |
| 11:25 | rhickey | the dynamism is the trick, these things can't become names Java classes |
| 11:25 | rhickey | named |
| 11:25 | cemerick | Chouser: agreed. |
| 11:25 | rhickey | cuper is bad design |
| 11:26 | rhickey | super |
| 11:26 | cemerick | rhickey: neither are scala traits, actually. They produce an interface of the same name as the trait, though (along with some generated name that scalac picks up). |
| 11:26 | Chouser | the set of fns to define the abstract base should be static things anyway, right? so a special form ... well, ok. We don't have to support it, but nothing about the broad design for mixins as described prevents them from working. |
| 11:27 | Chouser | hm, actually, I make kind of mixin macro for proxy recently. |
| 11:27 | Chouser | made |
| 11:30 | Chouser | I guess I mentioned it before -- allowed me to define a new anonymous class that inherited from APersistentMap, mixed in some calls to a delegate instance of PersistentHashMap, and then provide implementations for methods from another interface. |
| 11:30 | Chouser | new anonymous class via proxy |
| 11:31 | Chouser | so, um, it worked fine. :-) |
| 11:32 | rhickey | dynamic anonymous is fine, dynamic named (class or interface) is the problem |
| 11:33 | Chouser | right. so these mixins would be named and therefore not be a class or interface |
| 11:33 | rhickey | so the code we want to reuse has to be either a) staitc (e.g. genclass) or b)unnamed (e.g. not named by class/interface name) |
| 11:36 | rhickey | What's interesting about all of the abstract bases in Clojure is that there is/are corresponding interface(s) (as there should be) |
| 11:41 | rhickey | so if a newnew instance, acting as a prototype for an abstract base, sitting in some var, had an accessible (though anonymous) name (as suggested by cemerick earlier), then a 'mixing' newnew class could just declare it implements the same interfaces. Constructors are a problem though |
| 11:42 | rhickey | newnew hides ctors |
| 11:43 | Chouser | the mixing class's undefined methods would call methods of the mixed instance? wouldn't the mixing instance have to be passed in somehow? |
| 11:43 | cemerick | there's still one under the covers, though. The mixing class just provides super-args that align with those defined by the referenced newnew instance...? |
| 11:44 | cemerick | Chouser: implicit first 'this' arg? |
| 11:45 | Chouser | cemerick: but that would be a different method signature than defined by the interface its supposedly implementing! |
| 11:47 | cemerick | I really do mean *implicit* -- you'd use the 'this' symbol in the method impl, but the compiler would have to drop in the reference to the callee. |
| 11:47 | Chouser | I was assuming a mixed thing would have to be something like a class full of static methods whose names and signatures were similar (but not identical) to the instance methods defined by an interface. |
| 11:48 | cemerick | I don't think the resulting newnew concrete instance would be usable from a Java interface standpoint in that case. |
| 11:49 | cemerick | (not without a proxy object in front setting up the calls properly) |
| 11:49 | Chouser | cemerick: but drop that reference in where? if the method signature is defined by a public named interface, how do you pass in anything other than the "template" instance for the mixed class? |
| 11:50 | yason | Q: is there byte-seq that is analogous to line-seq ? |
| 11:50 | Chouser | yes, it would be a pain to try to use mixable class from java code |
| 11:51 | cemerick | my understanding is that the "template" instance wouldn't be used at all -- only its anonymous yet named class, with method impls, would be used. |
| 11:51 | cemerick | The template instance is just a handle that allows newnew to get a hold of the abstract base/mixin class. |
| 11:51 | cemerick | (at least how I'm reading rhickey's last msg) |
| 11:52 | Chousuke | urgh, for is a complex beast :P |
| 11:52 | cemerick | yason: how would such a thing segment the byte stream? |
| 11:53 | Chouser | ahh... so when given a mixable, newnew would ... copy the method implementations directly into the mixing class def? |
| 11:53 | rhickey | Chouser: the point I was trying to make about interfaces was that if the mixing-in class only fulfills the same contract as implied by the interfaces shared with the mixin, then the mixin can make blind calls to those methods, knowing they will call the most-derived versions |
| 11:53 | Chouser | oh! |
| 11:54 | rhickey | this is the non-macro-like solution, a macro-like one would eschew inheritance completely, just insert code when needed. There, as I said, I think calling super is bad and unnecessary |
| 11:54 | yason | cemerick: what do you mean? |
| 11:55 | cemerick | yason: line-seq breaks up text by line breaks -- how would you want to break up your InputStream? |
| 11:55 | yason | cemerick: I'm thinking of a lazy sequence of bytes read off from some reader |
| 11:55 | cemerick | or, InputStream's bytes |
| 11:56 | Chouser | so a mixing newnew class would actually inherit from the anonymous class of the mixable newnew? |
| 11:56 | rhickey | Chouser: right |
| 11:57 | rhickey | the tricks are: making newnew generate an abstract class when needed, constructors |
| 11:57 | rhickey | a newnew class gets a ctor, (as does a fn) that takes as args all closed-over locals, but Clojure completely hides that |
| 11:58 | cemerick | rhickey: and my point about ctors is that, the mixable class has ctors (or factory fn, whatever), and the mixing class can just pass super-args as one would with proxy today |
| 11:58 | rhickey | were the newnew class be used as a base, there's have to be some access to that ctor |
| 11:59 | rhickey | cemerick: factory fn is not a ctor, can't be used to init a base |
| 11:59 | rhickey | exposing the implicit ctor gets icky quickly |
| 11:59 | cemerick | right, right |
| 12:00 | cemerick | how so? |
| 12:01 | rhickey | because some aspects of the mixin prototype would have to be 'free' names |
| 12:01 | Chouser | the fields for closed-over values could be mutated once during creation. just mark them final but change them once anyway. |
| 12:02 | rhickey | e.g. (def x (let [a 42 b 57] (newnew [Object] [] ...)) |
| 12:03 | rhickey | a and b will be args to the class iff they were used in the impl, as would any other closed over things, might not be one level. No way to say, I want a to always be 42 and b to be a variable thing supplied by derivee |
| 12:04 | rhickey | Chouser: that abuse of final is in conflict with the optimizer - you might not see that new value in all threads |
| 12:05 | Chouser | rhickey: already ducked. |
| 12:05 | rhickey | just saying, it's not an option even |
| 12:06 | cemerick | Chouser: I already made rhickey blush talking about mutable fields a week or two ago ;-) |
| 12:07 | rhickey | mutable fields unlikely too, the only arg for them is the multiple allocation cost for something like AtomicReference or clojure.lang.Box |
| 12:08 | rhickey | but the lowest-level Java code of Clojure does use mutable fields, with all needed precautions synchronization-wise |
| 12:09 | Chouser | to specify mutable fields you'd have to give up some of the beautiful simplicity of closing over a regular local for all your state. |
| 12:09 | cemerick | rhickey: not so. I want to stop having to think about massive intermediate garbage produced while (for example) reducing a struct through transformation. Right now, in order to avoid that, I need to manually pull out the fields of interest and loop through them, and then create a struct at the end using the transformed values. |
| 12:10 | cemerick | that said, I'm certainly not expecting there to ever be a path provided for that sort of thing in clojure. |
| 12:10 | rhickey | cemerick: heh, I just designed one yesterday |
| 12:10 | cemerick | oh? |
| 12:11 | rhickey | batch updatable persistent data structures with O(1) conversion to/from mutability |
| 12:11 | rhickey | a.k.a. the holy grail |
| 12:12 | rhickey | not a joke, I've been working on the vector all day yesterday and during this discussion today |
| 12:12 | cemerick | vector? |
| 12:12 | cemerick | nah, I'm getting punk'd. |
| 12:13 | rhickey | yes, PersistentVector replacement |
| 12:14 | Chouser | only abstract newnews would need constructor args, right? |
| 12:14 | rhickey | you can say (let [v! (mutable v)] ...) and get a non-sharable vector supporting conj!, nth!, pop!, when you are done call (immutable v!), both mutable and immutable are constant-time |
| 12:14 | cemerick | I'm lured in. If that's true, then that can be newnew's fields AFAIC |
| 12:15 | rhickey | Chouser: all newnews that close over have ctors, it's a matter of exposure, usually the enclosing factory fn calls the ctor implicitly |
| 12:15 | cemerick | rhickey: assoc as well? |
| 12:15 | cemerick | assoc!, perhaps? |
| 12:15 | rhickey | cemerick: yes assoc! |
| 12:16 | rhickey | the general design will apply to all of the data structures except lists |
| 12:16 | dysinger | interesting |
| 12:16 | Chouser | rhickey: right, I mean only abstract newnews would need to be able to expose ctor args so that a derived class could pass them in explicitly. |
| 12:16 | rhickey | Chouser: right |
| 12:16 | Chousuke | rhickey: non-sharable meaning what? is it allowed to leave the scope it's created in? :) |
| 12:17 | rhickey | Chousuke: meaning, if you share copies of it you are in trouble and I can't help you |
| 12:18 | rhickey | but the updating is open in general, and after an immutable call I can help you, preventing any further mutation |
| 12:18 | Chouser | so an abstract newnew would list formal ctor args (that would actually be added to the full list of args for closed-over things) and a concrete newnew would list actual values to be passed in to the base class. |
| 12:19 | Chouser | hm, I guess an abstract base class may itself want to derive from another abstract and be able to pass in ctor values. |
| 12:19 | rhickey | Chouser: yes, it gets ugly |
| 12:19 | rhickey | I'm not sure bending newnew to do this is the right thing |
| 12:20 | rhickey | Chousuke: the prototypical 'right' use case is something like into, or other birthing situations, where no one will see the interim values |
| 12:20 | Chouser | Would you guess that abstract bases will only rarely want to close over locals? |
| 12:21 | rhickey | Chouser: I was going to say that before, but I'm not sure - it's the nice way to build in constants |
| 12:22 | Chouser | because of course as soon as you're closing over things, you want something very much like newnew |
| 12:22 | rhickey | Chouser: exactly |
| 12:25 | Chouser | I really like the kind of dynamism this would allow. redef the base newnew and you actually get a new anon class, so all existing derived instances continue to behave as they had. But new derived instances will see the new base class and pick up the new behavior. |
| 12:26 | rhickey | Chouser: yes, I'd love to keep the static-ness to interfaces, which are still critical |
| 12:27 | rhickey | Chouser: another sticking point is that newnews would normally only have one ctor, many abstract bases have multiple ctors |
| 12:30 | Chouser | ew |
| 12:31 | cemerick | is that at all necessary for cinc? |
| 12:45 | beutdeuce | i'm getting an unquote is unbound illegalstateexception. What does that mean? |
| 12:46 | Chouser | it means you're using ~ outside of a ` |
| 12:46 | beutdeuce | how do i epxress inequality? |
| 12:46 | beutdeuce | express* |
| 12:46 | Chouser | ,(not= 2 3) |
| 12:46 | clojurebot | true |
| 12:46 | beutdeuce | k, thnx |
| 12:50 | beutdeuce | any idea why i'm getting a stack overflow? => http://pastie.org/548306 |
| 12:53 | cemerick | very strange here: I've got a slot with the value: (delay (apply merge (pmap expensive-map-producing-fn [somedata]))) When I use pmap, the resulting delay contains an *empty map*. When I use map, I get correct results. |
| 12:54 | cemerick | beutdeuce: you're calling length recursively |
| 12:54 | beutdeuce | cemerick: it should stop when there is no more xs |
| 12:55 | maacl | beutdeuce: you are checking against nil |
| 12:55 | maacl | (= nil "") |
| 12:55 | maacl | ,(= nil "") |
| 12:55 | clojurebot | false |
| 12:56 | beutdeuce | what does the , mean? |
| 12:56 | maacl | beutdeuce: it asks clojurebot to evaluate |
| 12:56 | beutdeuce | ah, cool |
| 12:58 | beutdeuce | no, i need to check if xs is not equal to zero |
| 12:58 | beutdeuce | which is not= |
| 12:58 | beutdeuce | but for some reason, it never equals zero, which it should |
| 13:02 | technomancy | beutdeuce: the empty list is not nil in clojure |
| 13:02 | technomancy | ,(nil? ()) |
| 13:02 | clojurebot | false |
| 13:02 | technomancy | ,(empty? ()) |
| 13:02 | clojurebot | true |
| 13:02 | beutdeuce | ah |
| 13:02 | beutdeuce | empty? |
| 13:02 | technomancy | beutdeuce: that's what you want instead |
| 13:03 | beutdeuce | ajd to check if it isnt empty? |
| 13:03 | beutdeuce | and* |
| 13:03 | cemerick | ,(seq '(1 2)) |
| 13:03 | clojurebot | (1 2) |
| 13:03 | cemerick | ,(boolean (seq '(1 2))) |
| 13:03 | clojurebot | true |
| 13:03 | cemerick | ,(not (empty? '(1 2))) |
| 13:03 | clojurebot | true |
| 13:04 | beutdeuce | is there a ! in clojure to signify not? |
| 13:05 | technomancy | beutdeuce: ! has different connotations in clojure; it usually signifies side-effects |
| 13:05 | beutdeuce | i see |
| 13:05 | technomancy | not is just not |
| 13:06 | beutdeuce | k |
| 13:10 | rhickey | http://java.sun.com/javaone/2009/rockstars.jsp |
| 13:12 | technomancy | rhickey: nicely done. |
| 13:12 | rhickey | it's good for Clojure to get on the radar in such a mainstream venue |
| 13:13 | hiredman | rockstars? you'll need to beadazzle your jeans |
| 13:13 | technomancy | \m/ |
| 13:16 | technomancy | so what's the deal with this guy who's doing his own (non-clojureCLR) C# port of Clojure? http://code.google.com/p/ajlisp/updates/list |
| 13:23 | beutdeuce | lol, now i get a null pointer |
| 13:26 | Chouser | thats a whole lot of rockstars. |
| 13:30 | Chouser | does the JVM disallow operations in ctor before calls to super's ctor? Or is that a Java-level thing? |
| 13:33 | liebke | rhickey: I was just reading your JavaOne slides (linked on the rock stars page), do you have a copy of them on a public server that I can point people to (without registering with the SDN)? |
| 13:34 | rhickey | technomancy: I really wish he wouldn't call that Clojure |
| 13:34 | rhickey | I also pointed him to Clojure CLR, but he's persisted |
| 13:34 | Chouser | You asked him to stop using the name and he's refusing? |
| 13:35 | rhickey | No, I haven't asked him to stop yet, will do |
| 13:35 | technomancy | I hate that I'm saying this, but it might be wise to register the trademark if you haven't. =\ |
| 13:36 | rhickey | technomancy: yes, I've been looking at what Python does with theirs, seems sensible |
| 13:37 | rhickey | there are some issues with trademarks and some distros, no? |
| 13:38 | technomancy | yes, but IIRC only trademarked artwork |
| 13:38 | rhickey | unfortunately just more work and expense for me |
| 13:38 | technomancy | it's a shame that it's necessary |
| 13:38 | technomancy | the free software community has a great legal framework for dealing with copyright, but nobody has really got anything satisfying for trademarks |
| 13:39 | Chouser | it may not be necessary. his project is more likely to get long-term traction under its own name than if its generally known as a bad-will knock-off. |
| 13:44 | technomancy | python's guidelines seem pretty sane |
| 13:45 | Lau_of_DK | rhickey: Alternatively if you want to navigate around a potential legal battle, you could rename Clojure to .. oh I dont know.. Clabango? :) |
| 13:46 | danlarkin | *rimshot* |
| 13:47 | cemerick | The Clojure Foundation is not far away, I guess. |
| 13:47 | Chouser | bleh |
| 13:49 | Chouser | budgets board meetings elections lawyers |
| 13:49 | beutdeuce_ | technomancy: ruby's are even more :) |
| 13:50 | cemerick | Chouser: the ways of the world |
| 13:50 | clojurebot | Why are you asking *him* |
| 13:50 | Lau_of_DK | hehe |
| 13:50 | Lau_of_DK | nasty bot |
| 13:53 | beutdeuce_ | how would one go about creating custom types/type classes in clojure? |
| 13:55 | Chouser | beutdeuce_: most of the time it's best to just provide a bunch of functions that operate on a map or vector of a particular shape |
| 13:56 | beutdeuce_ | Chouser: i see, so clojure takes a different approach than haskell. |
| 13:57 | dnolen | beatdeuce_: if you need something complex/fancy look at multimethods, add-hoc hierarchies, and metadata. |
| 13:58 | Chouser | and if you need to provide an interface that can be used by regular Java code, look at gen-interface and proxy |
| 14:02 | cemerick | this map/pmap behaviour is *really* strange -- only manifests itself when the pmap call is made on the AWT event thread |
| 14:20 | hiredman | cemerick: please continue |
| 14:21 | cemerick | hiredman: not sure what else to say beyond what I've said already. I'm pretty baffled. |
| 14:21 | hiredman | must have missed the initial explanation |
| 14:22 | cemerick | very strange here: I've got a slot with the value: (delay (apply merge (pmap expensive-map-producing-fn [somedata]))) When I use pmap, the resulting delay contains an *empty map*. When I use map, I get correct results |
| 14:22 | hiredman | ~def delay |
| 14:22 | hiredman | eep |
| 14:22 | hiredman | thats not right |
| 14:23 | hiredman | ~def clojure.lang.Delay |
| 14:24 | hiredman | ~def pmap |
| 14:30 | hiredman | hmmm |
| 14:32 | hiredman | have you tried just wrapping in a closure? |
| 14:33 | cemerick | hiredman: not yet, but I'll throw that dart at the wall next :-/ |
| 14:33 | hiredman | :/ |
| 14:34 | cemerick | Debugging facilities leave much to be desired. Thankfully, nearly all of our fns are small and functional enough that reasonable unit testing is straightforward and almost-uniformly successful. |
| 14:42 | hiredman | ,(doc merge) |
| 14:42 | clojurebot | "([& maps]); Returns a map that consists of the rest of the maps conj-ed onto the first. If a key occurs in more than one map, the mapping from the latter (left-to-right) will be the mapping in the result." |
| 14:43 | ataggart | it would be neat to have a dummy stack be created during execution that holds the clojure line numebrs etc (like clojure.test's output). just for testing/.debugging of course. |
| 14:44 | cemerick | rhickey: did you say something before about how you'd like to have reified environments? |
| 14:45 | Chouser | the java stack holds clojure line numbers |
| 14:46 | Chousuke | I guess macros complicate the line number stuff? :/ |
| 14:47 | Chouser | I would think so. might even be hard to define what you'd want |
| 14:47 | Chouser | I suppose CL has addressed this |
| 14:48 | cemerick | hiredman: wrapping the pmap call (and the entire body of the delay) in a thunk had no effect |
| 14:48 | cemerick | nor did eliminating the delay |
| 14:48 | hiredman | weird |
| 14:48 | hiredman | is your function creating non empty maps? :P |
| 14:48 | ataggart | what reason, other than caching, would one use delay/force vs quote/eval |
| 14:49 | cemerick | heh |
| 14:49 | cemerick | hiredman: note that the results are perfect over the same dataset if I change pmap -> map |
| 14:49 | Chousuke | ataggart: why would you use quote/eval if delay/force works? :/ |
| 14:49 | hiredman | well |
| 14:49 | cemerick | you're saying I just *think* it's the same dataset? :-) |
| 14:49 | ataggart | premature optimization :( I should know better, but... |
| 14:49 | hiredman | cemerick: I dunno |
| 14:50 | hiredman | it seems like a good idea to check |
| 14:50 | hiredman | put some logging in expensive-map-producing-fn |
| 14:50 | cemerick | yeah, I did that first, actually :-) |
| 14:50 | hiredman | at it is producing the expected results |
| 14:53 | cemerick | as far as I can tell. |
| 14:53 | cemerick | I'm sure I'm the one screwing up, but it's convenient to think that might not be the case for a little while. |
| 14:54 | lisppaste8 | ataggart pasted "logging functions (jul, log4j, commons)" at http://paste.lisp.org/display/83697 |
| 14:54 | ataggart | if anyone thinks that's worth sticking contrib, be my guest |
| 14:54 | ataggart | my CA is in the mail |
| 15:13 | mebaran151 | ataggart, that actually looks really nifty |
| 15:13 | ataggart | thx |
| 15:14 | ataggart | needs better documentation and examples tho |
| 15:15 | ataggart | I just didn't want to write an actual logging system, there's enough of those around |
| 15:18 | cemerick | ataggart: yeah, that's pretty decent. Why bother with the separate commons-logging bit though, since you have separate support for log4j and java.util.logging? |
| 15:23 | ataggart | because commons-logging has its own config stuff, and some companies (like mine) use it |
| 15:23 | ataggart | my approach was not to mess with whatever someone might have already been doing in their java code |
| 15:24 | cemerick | oh, I didn't realize c-l had much of any configuration available...it's been a long time since I've touched it |
| 15:24 | ataggart | also cl supports other logging systems |
| 15:24 | ataggart | e.g. slfj |
| 15:24 | ataggart | whic I didnt get around to adding |
| 15:26 | ataggart | I did really enjoy the fact tat it was way easier to check for the presence of a lib with clojure than what the cl folks had to do in java |
| 15:27 | cemerick | is slfj used much at all? |
| 15:27 | cemerick | (I've never seen it in the wild) |
| 16:23 | Chouser | I'd really like class name aliases |
| 16:24 | Chouser | I guess this case is bad enough a macro would help... |
| 16:29 | cemerick | hiredman: and the winner goes to.....pmap + binding! |
| 16:30 | hiredman | huh |
| 16:30 | Chouser | yep, that would do it |
| 16:31 | rhickey | cemerick: what's the contest? |
| 16:31 | Chouser | binding is really quite pernicious in a thready, lazy, closury environment |
| 16:31 | cemerick | rhickey: I had a pmap call that was failing in a UI context, but not in a headless context, *and* it worked in the former if I switched to map. |
| 16:32 | cemerick | turns out I had a dosync floating around 50 stack frames up, so the threads used in the pmap couldn't see a variety of things. |
| 16:32 | cemerick | (only in the UI, that is) |
| 16:33 | cemerick | sorry, s/dosync/binding |
| 16:34 | maacl | Can anybody give me some hints on how to translate this: http://bit.ly/lcs_py (the backTrackAll function) from Python into Clojure? I have a pasted my attempt here: http://paste.lisp.org/+1SL7, but it doesn't work. For completeness I have included the functions required to test the traceback-all-lcs function and my traceback-lcs function (that works :-)). |
| 16:34 | cemerick | although the same issue would pop up in a transaction where some lower level used pmap, or futures, etc. |
| 16:35 | rhickey | cemerick: when something is running in a thread pool as utilized by pmap, I don't control the thread |
| 16:35 | rhickey | and it could be taking on work sent from multiple contexts |
| 16:35 | Raynes | technomancy: Is this a known bug? http://tr.im/sGhH |
| 16:35 | rhickey | so you really need a fn that captures any bindings you care about |
| 16:35 | cemerick | rhickey: yeah, I understand the dynamic, I had simply forgotten about the binding stuff that was going on in the UI |
| 16:36 | rhickey | I think there should be some with-captured-bindings macro |
| 16:36 | rhickey | yields a fn |
| 16:37 | rhickey | but generally I think people have this weird expectation that closures should know if they are going to run in other threads and do something different |
| 16:38 | cemerick | well, it's not so much that as a caller doesn't necessarily know what a fn is going to do w.r.t. parallelizing computation, and a fn can't always be sure of the environment it's going to be called in |
| 16:40 | cemerick | Just thinking about the pmap usages that I have floating around, I'm starting to get uneasy about the notion of using refs with (potentially) large transaction scopes for my cells reimpl. |
| 16:40 | technomancy | Raynes: not known by me, but I'm far from an expert on Windows |
| 16:40 | rhickey | I don't disagree that that is in conflict with dynamic binding generally, but pmap is explicit |
| 16:40 | rhickey | and pcalls, and future, and send |
| 16:40 | Raynes | technomancy: Happens here on Ubuntu Hardy as well. |
| 16:40 | Raynes | Apparently Brian Carper gets the same thing. |
| 16:41 | Chouser | does dosync use thread-local data? |
| 16:41 | Chouser | I suppose it must... |
| 16:41 | cemerick | rhickey: yeah, I'm just jabbering into the wind here. The tools are fine, I just need to apply them a little more consciously. |
| 16:41 | cemerick | yeah, transactions are thread-local until they've committed |
| 16:41 | rhickey | cemerick: not at all, I've been thinking about what would be a good alternative to dynamic binding for concurrent use |
| 16:41 | technomancy | Raynes: oh, right. I think the problem is actually that *in* is hooked up to the *inferior-lisp* buffer instead of the *slime-repl* buffer |
| 16:42 | Raynes | :o |
| 16:43 | Raynes | How would I go about changing that >_> |
| 16:43 | rhickey | there is a long-standing argument about inheritance of environments by sub-threads, but that is born of an old-styile use of manually launched threads. Thread pools ruin any notion of nesting or child thread |
| 16:43 | cemerick | rhickey: what would the options be in general, short of passing around environments between thread boundaries? |
| 16:44 | cemerick | s/between/across |
| 16:44 | technomancy | Raynes: buffer interaction with slime doesn't fit well into the streams paradigm. I've only used read-line etc in conjunction with sockets from slime |
| 16:44 | rhickey | cemerick: closures would have to capture definition context of some sort |
| 16:46 | rhickey | not normally what you want wrt dynamic vars, but perhaps explicitly |
| 16:47 | rhickey | if an explicit construct could do that, and re-establish, it would cover all calls made by it. You would only need to do that in the fn you pass to pmap/agents etc |
| 16:48 | rhickey | (pmap #capture-this-env(foo %) xx) |
| 16:49 | rhickey | people really expect that of the anonymous fns they pass to pmap, but not in their defns |
| 16:49 | rhickey | even though they are the same |
| 16:50 | hiredman | basically a short cut for (binding [a 1] (let [a a] (fn [] (+ a 1)))) |
| 16:50 | hiredman | ? |
| 16:50 | rhickey | hiredman: not, it has got to do a real rebinding, so nested calls see the dynamic vars |
| 16:51 | hiredman | oh |
| 16:51 | rhickey | so (let [a *a*] #(binding [*a* a] ... |
| 16:51 | hiredman | yeah |
| 16:51 | rhickey | definitely macro-able |
| 16:52 | rhickey | the only trick is you want args usually |
| 16:53 | technomancy | Raynes: it looks like swank-clojure doesn't rebind *in* at all |
| 16:53 | rhickey | (pmap (fn-with-bindings [*a* *b* *c*] [x] ...) xs) |
| 16:53 | rhickey | fn-capturing |
| 16:54 | rhickey | (fn-capturing [vars] [args] body) |
| 16:54 | rhickey | or something |
| 16:54 | kotarak | (defmacro bound-fn [vars args & body] (let [gens (take (count vars) (repeadetly gensym))] `(let ~(vec (interleave gens vars)) (fn ~args (binding ~(vec (interleave vars gens)) ~@body))))) |
| 16:54 | rhickey | right |
| 16:55 | cemerick | hrm |
| 16:55 | technomancy | Raynes: doesn't quite do what you want, but it might be a start: (binding [*in* (java.io.BufferedReader. (:reader swank.core.connection/*current-connection*))] (read-line)) |
| 16:55 | rhickey | cemerick: I understand you may not know what vars are used by nested calls |
| 16:55 | cemerick | yeah, that's one big issue |
| 16:56 | rhickey | that is a separate problem, I might be able to make capturing all bindings cheap |
| 16:56 | rhickey | the bindings system is designed to make that possible, or at least was |
| 16:56 | rhickey | could still again, an implementation detail |
| 16:56 | cemerick | actually, that's the biggest issue, esp w.r.t. transactions. To do what I was thinking of doing, I'd need transactions to span multiple threads -- which, now that I've said it, doesn't seem like a good plan in general. |
| 16:57 | kotarak | maybe fn-with-bindings could go into c.c.core until something suitable is ready? |
| 16:58 | Chousuke | kotarak: you don't need the (take (count vars) ..) part actually. |
| 16:58 | Chousuke | interleave stops when it runs out of items in one of the seqs :) |
| 16:58 | kotarak | ah. Nice. Like map. |
| 16:59 | rhickey | cemerick: if those threads touch refs then you need help, if they just do computations then that's ok |
| 17:00 | cemerick | rhickey: so if they're doing computations on refs, they cancel each other out or something? |
| 17:00 | cemerick | ;-) |
| 17:00 | cemerick | no, my current/former plan is just wrongheaded. |
| 17:03 | cemerick | futures and pmap aren't really the "problem" -- it's not strictly necessary that I parallelize at that level of granularity. The real issue is using the cells impl in conjunction with a UI (where one will inevitably want to push some computation out to another thread). |
| 17:10 | rhickey | alrighty - http://github.com/richhickey/clojure/commits/master |
| 17:11 | rhickey | if someone would please implement push/pop/get-thread-bindings, this new (clojure.lang.Var/getThreadBindings) will get you a map ready to push |
| 17:12 | rhickey | this will let you build the (preferable) capture-all-bindings |
| 17:13 | rhickey | user=> (clojure.lang.Var/getThreadBindings) |
| 17:13 | rhickey | {#'clojure.core/*command-line-args* nil, #'clojure.core/*print-meta* false, #<Var: --unnamed--> 2, #'clojure.core/*ns* #<Namespace user>, #'clojure.core/*math-context* nil, #<Var: --unnamed--> #<DynamicClassLoader clojure.lang.DynamicClassLoader@7e28388b>, #'clojure.core/*2 nil, #'clojure.core/*warn-on-reflection* false, #'clojure.core/*3 nil, #'clojure.core/*1 {#'clojure.core/*command-line-args* nil, #'clojure.core/*print-meta* false, #< |
| 17:13 | cemerick | hmm, fancy |
| 17:14 | cemerick | the nice th |
| 17:15 | kotarak | (defmacro fn-with-bindings [args & body] `(let [bindings# (clojure.lang.Var/getThreadBindings)] (fn ~args (try (clojure.lang.Var/pushThreadBindings bindings#) ~@body (finally (clojure.lang.Var/popThreadBindings)))))) |
| 17:15 | kotarak | and |
| 17:16 | rhickey | binding* ? |
| 17:16 | rhickey | a finally without it's own try? |
| 17:16 | kotarak | A function doing what binding does accepting a map. |
| 17:17 | rhickey | tools are there to build that |
| 17:17 | kotarak | binding could then look like `(binding* ~(construct map here) (fn [] ~@body)) |
| 17:17 | rhickey | I'd prefer not to have Var/blah - these can become proper supported fns push/pop/get-thread-bindings |
| 17:17 | kotarak | rhickey: I know. I did several times... |
| 17:21 | kotarak | rhickey: this is what I had in mind: http://paste.pocoo.org/show/129042 |
| 17:22 | kotarak | But it throws.... which means it's possible to inject something. |
| 17:22 | rhickey | kotarak: I don't get it, what's the point of my-try? |
| 17:23 | rhickey | no user should be writing finally outside of try |
| 17:23 | rhickey | I think it is a non problem |
| 17:24 | kotarak | I don't know whether it is problem or not (most likely not). But if I expand a macro into a try wrapping some body I could inject accidentally some catch clause. |
| 17:24 | kotarak | What users should do and what they actually do, are two different things... But I agree that this is not really a problem. |
| 17:25 | rhickey | kotarak: I don't see how unless people are writing loose finally/catch clauses, else they would nest |
| 17:26 | rhickey | anyway, with getThreadBindings you can now have some fun |
| 17:26 | kotarak | (my-macro-expand-in-a-try (try (my-user-try)) (catch SomeException ...)) One wrong paren later.... but anyway. It is not a problem. Otherwise it would have already been mentioned... |
| 17:34 | Chouser | rhickey: has your interest in a finger-tree diminished with the vector stuff you were describing today? |
| 17:34 | clojurebot | rhickey is a T-1000 sent from the future |
| 17:58 | rhickey | Chouser: not at all, finger-trees will support fast insertion in the middle and concatenation, definitely different properties from vector |
| 17:58 | Chouser | ok |
| 17:59 | Chouser | just checking. :-) |
| 18:02 | Chouser | I guess I knew the properties were very different from a vector, but I wondered if you wanted a mutable/immutable style instead of an always-immutable persistent finger tree. |
| 18:03 | Chouser | ah, I misunderstood the api! |
| 18:36 | rhickey | Chouser: the immutable/mutable design is always based upon a pure immutable one |
| 18:44 | wavister | does anyone else have trouble building a fresh pull of clojure-contrib? |
| 18:45 | wavister | it says it can't locate walk.clj, which is part of the whole repo i just downloaded. seems like those paths should be set up correctly by default... |
| 18:46 | Chousuke | hm |
| 18:47 | Chousuke | didn't walk move to Clojure itself when c.c.test-is did? |
| 18:48 | Chousuke | that might be the cause of your breakage. walk is no longer in contrib. |
| 18:48 | Chousuke | if you're using contrib with 1.0.0, checkout the clojure-1.0-compatible branch |
| 18:49 | wavister | ahhh that must be the thing |
| 18:49 | wavister | or maybe i should get a newer clojure...? |
| 18:51 | hiredman | yeah |
| 18:55 | Chousuke | wavister: well, if you don't mind running clojure from git :P |
| 18:56 | wavister | i don't mind. |
| 19:00 | wavister | aaand it works! thanks! |
| 19:55 | rhickey | hmm, PersistentVector in batch mode slightly faster than j.u.ArrayList! |
| 19:56 | rhickey | sorry, Vector |
| 19:57 | rhickey | only slightly slow than ArrayList |
| 19:59 | rhickey | slower |
| 20:01 | schaefer_ | i'm curious why the following doesn't work: (def (with-meta (symbol (str 't)) {:t "t"}) "t") |
| 20:02 | schaefer_ | i get that def is a special form... is there a way to achieve the binding with a dynmically created symbol? |
| 20:02 | rhickey | schaefer_: def is a special form that doesn't evaluate its first argument |
| 20:03 | technomancy | maybe this: (eval `(def ~(with-meta (symbol (str 't)) {:t "t"}) "t")) ? |
| 20:03 | rhickey | ,(doc intern) |
| 20:03 | clojurebot | DENIED |
| 20:03 | rhickey | ,(doc intern) |
| 20:03 | clojurebot | DENIED |
| 20:03 | schaefer_ | i'm actually going through clojure.core and trying to understand why the defn macro works the way it does |
| 20:04 | technomancy | schaefer_: that works, but I don't think it's what you want wrt metadata |
| 20:04 | rhickey | why, clojurebot? |
| 20:04 | technomancy | (doc intern) |
| 20:04 | clojurebot | DENIED |
| 20:04 | technomancy | hiiiiiiiredman! |
| 20:04 | rhickey | ,(doc rest) |
| 20:04 | clojurebot | "([coll]); Returns a possibly empty seq of the items after the first. Calls seq on its argument." |
| 20:04 | rhickey | hrm |
| 20:04 | Raynes | (doc intern) |
| 20:04 | clojurebot | DENIED |
| 20:05 | schaefer_ | well, what i ultimately want is a way to define a function with some meta data attached to it. |
| 20:05 | technomancy | schaefer_: metadata on the function or on the var? |
| 20:06 | schaefer_ | good question. i think i want it on the var. i don't think you can attach metadata to a function (IFn does not subclass IObj) |
| 20:06 | technomancy | it's very tricky to do, in any case |
| 20:07 | schaefer_ | how about this: how would you go about attaching additional metadata to an existing var? |
| 20:08 | rhickey | it is definitely runtime metadata? |
| 20:08 | schaefer_ | rhickey: yes, but i'm curious as opposed to what? |
| 20:10 | rhickey | user=> (def #^{:my :meta} foo 42) |
| 20:10 | rhickey | #'user/foo |
| 20:10 | rhickey | user=> ^#'foo |
| 20:10 | rhickey | {:ns #<Namespace user>, :name foo, :file "NO_SOURCE_PATH", :line 16, :my :meta} |
| 20:11 | schaefer_ | can you translate that without the reader macros? i'm just learning and slowly coming up to speed |
| 20:11 | schaefer_ | i'm really trying to get the basics of all this stuff down |
| 20:13 | rhickey | schaefer_: well, the reader metadata is key, and thus the distinguishing point, getting at the meta can go like this: |
| 20:13 | rhickey | user=> (def #^{:when (java.util.Date.)} foo 42) |
| 20:13 | rhickey | #'user/foo |
| 20:13 | rhickey | user=> (meta (var foo)) |
| 20:13 | rhickey | {:ns #<Namespace user>, :name foo, :file "NO_SOURCE_PATH", :line 24, :when #<Date Thu Jul 16 20:12:55 EDT 2009>} |
| 20:14 | technomancy | I'm not sure you can do the def without reader macros |
| 20:15 | rhickey | #^amap a-symbol-or-collection will read one object with attached metadata |
| 20:15 | schaefer_ | ah... that could have been my failure when i was doing this on my own. i was trying to work it without the reader macro |
| 21:06 | arohner | ,(name (var map)) |
| 21:06 | clojurebot | java.lang.ClassCastException: clojure.lang.Var cannot be cast to clojure.lang.Named |
| 21:07 | Chouser | ,(.sym (var map)) |
| 21:07 | clojurebot | map |
| 21:07 | Chouser | ,(.ns (var map)) |
| 21:07 | clojurebot | #<Namespace clojure.core> |
| 21:07 | arohner | yeah, I knew the methods were on the var object |
| 21:08 | arohner | just found it weird that name didn't work on it |
| 21:08 | Chouser | yeah. me too. |
| 21:08 | Chouser | of course not all vars have a name |
| 21:08 | arohner | oh, right |
| 21:08 | rhickey | ArrayList |
| 21:08 | rhickey | time: 1129 |
| 21:08 | rhickey | PersistentVector (batch mode) |
| 21:08 | rhickey | time: 1354 |
| 21:08 | rhickey | smokin! |
| 21:08 | arohner | with-local-vars, etc |
| 21:10 | rhickey | was: |
| 21:10 | rhickey | PersistentVector |
| 21:10 | rhickey | time: 3547 |
| 21:11 | Chouser | wow |
| 21:12 | Chouser | much too fast |
| 21:12 | rhickey | truly crazy since you can, anywhere in the middle of the batch, say (immutable v) and get a perfectly fine immutable vector |
| 21:12 | rhickey | and then keep going |
| 21:12 | Chouser | does the immutable thing persist like a regular PVector? |
| 21:13 | rhickey | (immutable v) being instantaneous |
| 21:13 | rhickey | Chouser: the immutable thing _is_ a pvector |
| 21:13 | Chouser | oh, same internal structure? |
| 21:14 | rhickey | the mutable thing is almost a pvector, structurally similar so it can be 'adopted' as immutable in constant time |
| 21:14 | Chouser | ah! cool. |
| 21:14 | rhickey | precisely same node structure |
| 21:14 | rhickey | just added a bit for editing |
| 21:15 | rhickey | I'm still stunned at how fast the tree structure is |
| 21:18 | rhickey | those times are for a million writes and 10 million reads of a 100k item arraylist/pvector |
| 21:18 | rhickey | in ms |
| 21:18 | Chouser | is lazilypersistentvector still useful, or might you just as well build via mutable pvector at read time? |
| 21:18 | rhickey | Chouser: dunno yet, probably not needed now |
| 21:19 | rhickey | most readable things are small, its vec/vector/into that are going to blaze now |
| 21:20 | rhickey | and the mutable mode will be available for end user code as well |
| 21:22 | rhickey | I prevent: using the mutable thing with the normal fns (conj etc), immutability of result of immutable call. Only caveat is against sharing the mutable thing - I can't help you then |
| 21:22 | rhickey | er, protect immutability |
| 21:23 | Chouser | I really can't imagine it'll be a problem |
| 21:23 | Chouser | you can use ArrayList today if you want to |
| 21:23 | rhickey | exactly, but not with these features |
| 21:24 | Chouser | and if you accidentally forget to (immutable v) before you return it, either nobody will conj and it won't matter, or the case will be caught. |
| 21:24 | Chouser | right |
| 21:24 | Chouser | one key feature being exactly that latter protection. |
| 21:25 | rhickey | and the free persistent 'copy', no copy involved |
| 21:25 | Chouser | yep. that free copy is one key way this is different from, say, google protobuf builders. |
| 21:26 | rhickey | the pvector impl is still pretty gnarly |
| 21:26 | rhickey | :) |
| 21:26 | Chouser | heh. I don't doubt it. |
| 21:26 | rhickey | revisiting it was not fun |
| 21:26 | Chouser | oh! |
| 21:26 | Chouser | you mean the way it was previously was gnarly? |
| 21:26 | rhickey | yeah |
| 21:27 | rhickey | lots of bit shifts etc |
| 21:27 | Chouser | ah, yes. I know that very well. |
| 21:27 | Chouser | Doesn't look any better in JavaScript. |
| 21:28 | rhickey | one question is, how much functionality to put on the mutable versions, right now just basic assoc!, conj!, pop!, and nth! |
| 21:28 | Chouser | Kinda doubt it will in Clojure either. ;-) what it's *doing* is gnarly. |
| 21:29 | rhickey | Chouser: I was trying to figure out today why that was - it's a recursive structure, but the tail etc just make it a pile of edge cases |
| 21:29 | rhickey | also it grows up, not down |
| 21:31 | rhickey | in any case, these perf tests show it will be well worth it, no compromises in using Clojure's data structures |
| 21:32 | rhickey | I think the technique will generalize to any tree |
| 21:32 | rhickey | so once someone sees the vector, maybe they can do the maps |
| 21:36 | Chouser | will be well worth what? having made this improvement to vector so that it can compete even better against plain mutable structures? |
| 21:37 | rhickey | yes |
| 21:37 | arohner | yes, I can take advantage of a 3x performance boost |
| 21:39 | rhickey | also once you're at 1129/1354 you can say there's nothing substantial to be gained by switching to a java thingy |
| 21:39 | Chouser | didn't we say that anyway? ;-) |
| 21:40 | rhickey | Yes, I thought at 2-4x it was worth writing Clojure |
| 21:41 | rhickey | that was my target |
| 21:41 | rhickey | but the question becomes, once you have a bottleneck, do you have to leave everything good behind? |
| 21:42 | rhickey | switch code radically |
| 21:42 | Chouser | yeah, that's similar to your response at the meetup to my all-too-frequent joke about speed. |
| 21:42 | Chouser | and it's a good response |
| 21:43 | rhickey | my ultimate goal is to keep people away from Java mutable data structures other than j.u.concurrent, and away from arrays |
| 21:43 | rhickey | the latter is the toughest nut to crack |
| 21:43 | Chouser | arrays are going to be ... yeah |
| 21:44 | Chouser | vectors of primitives will help a lot of the memory-concious cases. |
| 21:44 | rhickey | but all of this (incl chunks) paves the way for vectors of primitives |
| 22:10 | mebaran151 | what would be the best way to test if a class is a given subclass of another class? |
| 22:10 | hiredman | well, if it's given then = should be fine |
| 22:11 | hiredman | if you just want to test if a class is a subclass of another class, you can use isa? |
| 22:11 | mebaran151 | isa? sounds right |
| 22:12 | mebaran151 | but (isa? Exception IllegalArgumentException) returns false |
| 22:12 | Chouser | other way 'round |
| 22:12 | skalnik | Clojure is awesome :) |
| 22:13 | Chouser | ,(isa? IllegalArgumentException Exception) |
| 22:13 | clojurebot | true |
| 22:13 | mebaran151 | thanks Chouser, I wish I'd put it the right way at first |
| 22:14 | Chouser | arg ordering is frequently hard to guess at |
| 22:14 | hiredman | (doc isa?) |
| 22:14 | clojurebot | "([child parent] [h child parent]); Returns true if (= child parent), or child is directly or indirectly derived from parent, either via a Java type inheritance relationship or a relationship established via derive. h must be a hierarchy obtained from make-hierarchy, if not supplied defaults to the global hierarchy" |
| 22:15 | _hrrld | It's so cool that the seq abstractions work on built-in Java objects. |
| 22:20 | Chouser | it's somewhat interesting that despite it's homoiconicity, the data structures used most commonly for runtime data are used least commonly to represent code. |
| 22:21 | _hrrld | Isn't that because most runtime data isn't code? ;) Everyone is choosing the structures most amenable to their data. |
| 22:23 | Chouser | yes, indeed! but that's rather different from older lisps. |
| 22:23 | Chouser | ...where you had a list, so you generally made so with that for both code and data. |
| 22:23 | _hrrld | Where the data structures to choose from were so much more limited. |
| 22:23 | _hrrld | yes. |
| 22:24 | Chouser | I don't think clojure's lists are particularly better at representing code than clojure's vectors would be. |
| 22:24 | _hrrld | What language will take advantage of array backed hash map tries for its code representation? |
| 22:25 | Chouser | Is the main benefit of using both lists and vectors in code that they stand out visually from each other? |
| 22:25 | hiredman | I was talking to someone in #scala a few months back that wanted a homoiconic language based on maps |
| 22:25 | hiredman | but he might have just been winding me up |
| 22:25 | Chouser | hehe |
| 22:25 | _hrrld | For nondeterministic evaluation! |
| 22:26 | arohner | hiredman: there's a language like that |
| 22:26 | arohner | it's a toy |
| 22:26 | Chouser | I've thought things like that before ... though I wanted order-preserving maps. |
| 22:26 | Chouser | ...and that was before I knew lisp or what homoiconicity was. |
| 22:26 | arohner | http://lambda-the-ultimate.org/node/2887 |
| 22:37 | mebaran151 | what would dispatch function look like that would choose a multimethod based on whether or not the selector was a parent? |
| 22:38 | mebaran151 | kind of like dispatching with type except it wouldn't be strict equality |
| 22:41 | hiredman | multimethods don't dispatch on strict type equality |
| 22:42 | hiredman | it wall walk up the hier if no equality exists |
| 22:42 | mebaran151 | no I know, I'm trying to come up with a function that would dispatch this way |
| 22:43 | mebaran151 | essentially I'm just passing in type right now as the dispatcher, but it would be nice to catch the class children too |
| 22:43 | hiredman | erm |
| 22:44 | hiredman | lisppaste8: url? |
| 22:44 | lisppaste8 | To use the lisppaste bot, visit http://paste.lisp.org/new/clojure and enter your paste. |
| 22:45 | lisppaste8 | hiredman pasted "defmulti example" at http://paste.lisp.org/display/83721 |
| 22:45 | hiredman | it sounds like that is what you want |
| 22:45 | mebaran151 | http://clojure.pastebin.com/m48244d1b |
| 22:46 | mebaran151 | but I don't know if it would catch the children too |
| 22:46 | hiredman | it will catch subclasses of those two exceptions |
| 22:46 | hiredman | but you do the same thing for both |
| 22:47 | mebaran151 | right now yes, but I'll handle them differently later |
| 22:47 | mebaran151 | how does multimethod determine the match? |
| 22:47 | hiredman | it checks for an exact match, then it walks the superclasses looking for a match |
| 22:48 | mebaran151 | I thought it used whatever the the function returned, kind of like a dispatch table. Are the rules more nuanced? |
| 22:48 | hiredman | yes |
| 22:48 | hiredman | it is also possible to use heirarchies other then the java Class heirarchy |
| 22:48 | mebaran151 | that was what I was looking for |
| 22:49 | mebaran151 | so you can pass it a custom set of rules for determining what to consider a super and subclass? |
| 22:49 | hiredman | ,(doc defmulti) |
| 22:49 | clojurebot | "([name docstring? attr-map? dispatch-fn & options]); Creates a new multimethod with the associated dispatch function. The docstring and attribute-map are optional. Options are key-value pairs and may be one of: :default the default dispatch value, defaults to :default :hierarchy the isa? hierarchy to use for dispatching defaults to the global hierarchy" |
| 22:49 | hiredman | no |
| 22:49 | hiredman | you pass it a hierarchy |
| 22:49 | hiredman | which it walks just like it would walk the Classes |
| 22:50 | hiredman | mebaran151: http://clojure.org/multimethods |
| 22:51 | mebaran151 | do the hierarchies always work in terms of symbols? |
| 22:52 | hiredman | if you look at the link I just pasted, you will see hierarchies using keywords |
| 22:55 | skalnik | ataggart: I got that infinite fib-seq done :) |
| 22:57 | mebaran151 | thanks hiredman, I think I conflated symbols and keywords |
| 22:58 | hiredman | ,(derive (Object.) :bob) |
| 22:58 | clojurebot | java.lang.Exception: Assert failed: (namespace parent) |
| 22:59 | hiredman | ,(doc derive) |
| 22:59 | clojurebot | "([tag parent] [h tag parent]); Establishes a parent/child relationship between parent and tag. Parent must be a namespace-qualified symbol or keyword and child can be either a namespace-qualified symbol or keyword or a class. h must be a hierarchy obtained from make-hierarchy, if not supplied defaults to, and modifies, the global hierarchy." |
| 23:00 | Raynes | (doc intern) |
| 23:00 | clojurebot | DENIED |
| 23:06 | mebaran151 | anyway, thanks hiredman. I'm pleasantly surprised the default behavior of clojure |
| 23:07 | hiredman | multimethods are very cool |
| 23:08 | grrrt | speaking of which... do all functions defined with defmethod need to be of the same arity? |
| 23:08 | Chouser | nope |
| 23:09 | grrrt | spiffy |
| 23:09 | hiredman | but the dispatch function needs to be able to handle all the arities |
| 23:09 | Chouser | but if the number of args you pass in cause either the dispatch fn or the method chosen to error out, then you get an error |
| 23:09 | grrrt | a dispatch fn could then be (fn [&args] ....) i suppose? |
| 23:09 | hiredman | (comp type first list) |
| 23:09 | Chouser | yep |
| 23:10 | grrrt | I needed that a couple of days ago, can't remember what for though. but that's really cool |
| 23:39 | gko | How to key of a value in a map? |
| 23:39 | gko | how to get key of value in a map? |
| 23:40 | gko | Oh... of course, no build in function, since value can be not unique.. |
| 23:40 | hiredman | ,(map #(.getName %) (.getMethods (class {}))) |
| 23:40 | clojurebot | ("seq" "withMeta" "withMeta" "withMeta" "assoc" "assoc" "valAt" "valAt" "entryAt" "without" "assocEx" "count" "iterator" "containsKey" "capacity" "empty" "empty" "create" "equiv" "invoke" "invoke" "get" "put" "hashCode" "clear" "equals" "toString" "isEmpty" "values" "size" "entrySet" "putAll" "cons" "remove" "keySet" "containsValue" "applyTo" "call" "applyToHelper" "throwArity" "invoke" "invoke" "invoke" "invoke" "invoke" |