# #clojure log - Jul 16 2009

The Joy of Clojure
Main Clojure site
IRC
List of all logged dates

0:30 Knekk: bleh, using the Emacs Starter Kit messes up my arrow keys

0:44 ataggart: is there a macro somewhere that will evaluate forms until one returns logical true?

0:45 I seem to recall it, but can't find it

0:49 aha! (some eval ['(foo ...) '(bar ...) '(baz ...)])

0:50 skalnik: Learning Clojure has given me a headache :D

0:50 ataggart: thats the feeling of new pathways being burrowed through your brain.

0:50 skalnik: yep!

0:51 I've given up on Project Euler problem #2 for now :)

0:51 ataggart: do you have a fib function?

0:52 skalnik: yep, that works :)

0:53 I got stuck on trying to create a set of all the fib values under 4 million

0:53 ataggart: (reduce + (filter even? (take-while #(<= % 4e6) (fib-seq))))

0:53 booyah

0:54 skalnik: Haha

0:54 Why reduce instead of apply?

0:54 What's the difference

0:54 (if any)

0:55 ataggart: lazy

0:55 skalnik: ah

0:55 ataggart: I think

0:55 hmm

0:55 no cant be about laziness

0:56 since you have to run thru the whole list

0:56 * ataggart looks up the implementing code

0:56 ataggart: but "reduce" is more desciptive

0:56 skalnik: Now I just need to generate fib-seq

0:56 I agree :)

0:57 ataggart: ah ok, apply would work but only because + can take any number of args

0:58 reduce is when you have a function that takes two args

0:58 and crepeatedly applies it

0:58 skalnik: ah ha

0:58 So what I want to do for fib-seq is (map #(fib %) (range 10)), but replace 10 with a very large number

0:59 However, that seems wasteful

0:59 ataggart: hmm not sure how you're trying to apprach it. doesn't sound right

1:00 skalnik: so I have a fib function, so if you call (fib 3) it gives the third fibonacci number

1:00 The next step I wanted to do is create a set of fibonacci numbers less than 4 million

1:00 hiredman: someone should just write a canonical fib function so everyone can get on with their lives

1:01 ataggart: no one that needs to write one is doing for any reason than to learn. be gentle.

1:02 skalnik, the thing is you don't want a function that takes an index

1:02 you want a function that will return a sequence of all values

1:03 skalnik: I suppose

1:03 I haven't learned that much though, and I'm quite tired, plus I have surgery tomorrow, so I should be heading to bed :)

1:03 Thanks for the help ataggart

1:04 also, my method works, just seems improper to me :)

1:04 ataggart: your intuition is correct ;)

1:09 skalnik: Very cool that no matter what large number I plug in, execution time is still roughly the same :)

1:09 now, I really should be off to bed.

3:08 hiredman: clojurebot: CA

3:08 clojurebot: CA is Contributor Agreement: http://clojure.org/contributing

3:27 ataggart: mailed mine today

5:27 Jomyoot: (with-query-results rs ["select * from blogs"]

5:27 Does that have lisp syntax for select statement?

5:27 http://en.wikibooks.org/wiki/Clojure_Programming/Examples/JDBC_Examples shows lips syntax for other commmadns but not select

5:55 How do I output Clojure's vector of int from Java?

5:56 I am in Java. I want to output Vector of int, that is usable by Clojure

5:56 jdz_: use iterators?

5:56 jdz: or you can convert a vector to array

5:57 Jomyoot: Clojure has [1 2 3 4]

5:57 vector

5:58 I want to output [1 2 3 4] from Java object. that will work as [1 2 3 4] in Clojure with no conversion

5:58 Is that possible?

5:58 jdz: what do you mean by "output"?

5:59 Jomyoot: return

5:59 return from Java method

6:00 jdz: Clojure vectors are Java objects (as any value in Clojure)

6:00 you can pass them and retourn to any Java methods you want

6:00 Jomyoot: hmm

6:00 jdz: I don't really understand what you want

6:01 Jomyoot: Vector<Integer> vect= new Vector<Integer>(100); return vect;

6:01 How would Clojure see vect?

6:02 jdz: as an instance of Vector class

6:02 Java Vector class

6:02 Jomyoot: hmm

6:03 What would I output from Java then If I want Clojure to see as native [1 2 3 4]

6:03 Clojure's native

6:03 rottcodd: Jomyoot: I think you want to create a clojure.lang.PersistentVector

6:03 jdz: ,(class [1])

6:03 clojurebot: clojure.lang.LazilyPersistentVector

6:05 Jomyoot: Is it a standard practive to create a LazilyPersistentVector in Java. then return to Clojure?

6:05 As means of interop between java/clojure?

6:06 jdz: k

6:06 sorry

6:06 (that was C-x k in the wrong window)

6:07 Jomyoot: you can use all the classes and interfaces in src/jvm/clojure/lang from Java

6:09 Jomyoot: Arrays

6:09 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.

6:09 Why ? what's wrong with using arrays?

6:11 jdz: they are not persistent

6:11 and there is nothing wrong with them

6:11 you just have to be very careful if you are using them

6:11 that's why the rule of thumb of using them only for interop holds

6:13 Jomyoot: Is there easy ways to quickly convert array of int or vector of int to Clojure's native data?

6:16 rottcodd: vec

6:16 cark: or seq

6:17 Jomyoot: seq

6:17 i want to make seq out of java array of vector

6:17 possible?

6:17 must be done manually?

6:18 or is there built in method

6:18 cark: just use the seq function on your array

6:19 Jomyoot: seq also works on Strings, native Java arrays (of reference types)

6:19 what is reference types?

6:19 is int = reference types?

6:19 jdz: ,(map (partial + 1) (seq (into-array [1 2 3 4 5])))

6:19 clojurebot: (2 3 4 5 6)

6:20 cark: i think in this context reference type means objects as opposed to primitive values

6:20 Jomyoot: hence array of integer will not work

6:20 jdz: i just showed you that it works

6:21 cark: ,(seq (make-array Integer/TYPE 10))

6:21 clojurebot: (0 0 0 0 0 0 0 0 0 0)

6:21 jdz: in the example above a native Java array is created from Clojure vector, and then back to a sequence

6:21 Jomyoot: is 1 2 3 4 in Clojure represented as int or Integer in Java?

6:21 cark: it depends

6:22 these are boxed before doing function calls, but could be native integers inside a function

6:22 also you can use native integers in java array

6:23 so there would not be any boxing on function calls i guess

6:23 ChrisPS: ,#'in-ns

6:23 clojurebot: #'clojure.core/in-ns

6:30 cark: Jomyoot : native integer arrays do work with seq ...but the seq will contain boxed integer in the end

6:30 ,[(into-array [1 2 3 4 5]) (make-array Integer/TYPE 10)]

6:30 clojurebot: [#<Integer[] [Ljava.lang.Integer;@1b206f> #<int[] [I@ee90e9>]

6:31 Jomyoot: well then

6:31 cark: as you can see the second form creates an array of native ints

6:32 Jomyoot: does it matter in clojure wheter it's native or boxed int?

6:32 cark: no more than it matters to java

6:32 Jomyoot: do clojure functions that require ints care?

6:32 well then it does matter then

6:32 cark: the only difference to my knowledge is performances

6:32 Jomyoot: some methods expect boxed some expect native

6:33 i would assume that most clojure functions expect either native or boxed but not both

6:33 cark: have you an example of a java function that expects native ?

6:33 i'm not knowledgable at all on the java side of things, looks like something interesting to test

6:34 jdz: many methods that accept characters also accept ints

6:34 Jomyoot: more methods expect int[] than Integer[]

6:34 jdz: like, Character/isDigit or something

6:34 Jomyoot: more methods expect int than Integer as well

6:34 and all number literals in Java are native

6:34 not boxed

6:35 cark: ,(Character/isDigit (new Integer 3))

6:35 clojurebot: false

6:35 cark: that's a boxed integer i'm passing there

6:36 it is my beleif that the vm takes care of the conversion

6:36 Jomyoot: ,(Character/isDigit 1)

6:36 clojurebot: false

6:37 jdz: ,(Character/isDigit (int \5))

6:37 clojurebot: true

6:37 cark: though i don't think that applies to arrays

6:37 keca: ,(Character/isDigit (int \5))

6:37 clojurebot: true

6:37 cark: ,(Character/isDigit (Integer. (int \5)))

6:37 clojurebot: true

6:37 grrrt: ,(int \5)

6:37 clojurebot: 53

6:37 jdz: 5 is *the* number :)

6:37 keca: ,\5

6:37 clojurebot: \5

6:39 grrrt: isDigit has two implementations: one with a char and one with an int

6:39 the int version assumes the int you pass in is a unicode codepoint

6: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])

6: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?

6:44 grrrt: if you get a FileNotFoundException, you're trying to load the class as a file?

6:45 it should be on your java classpath, then you can use 'import' to use it in your namespace

6:45 cark: you need to start java with the path to your jar added to the classpath parameter

6: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

6:46 ah, import... ok, let me give that a shot

6:46 grrrt: yeah use is for clojure namespaces

6: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

6:49 grrrt: every jar file

6:49 cark: you can use wildcards for jvm 1.6

6:49 grrrt: ah yes, that's true

6:49 heh. spent too long on old jvms :(

6: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

6:50 mikem`: grrrt: ok, that's what I needed: use import and specify all jars on the command line. thanks!

6:50 grrrt: no worries, have fun!

6:50 cark: note that there is only one star

6:50 mikem`: cark: the wildcard doesn't seem to work for me

6:51 cark: what's your jvm version ?

6:51 grrrt: mikem`: if you do 'java -version', what do you get?

6:51 mikem`: 1.6

6:51 grrrt: hm

6:51 cark: i swear it works =P

6:51 mikem`: 1.6.0_14

6:51 cark: I'm probably doing it wrong...

6:51 * mikem` tries a few more variations

6:51 cark: i have like 8 libraries in there in use for this project

6:52 grrrt: you'd normally start your stuff from a shell script, ant file or maven file anyways

6:52 you wouldn't use a plain "java ............ clojure.main" command every time

6:52 cark: ahwell the end product is an executable jar

6:53 so there's no scrip, and no customer mess up =)

6:53 grrrt: heh

6:54 mikem`: cark: ok, with this command, everything works: http://dpaste.com/67796/

6:55 cark: great then =)

6:55 though it might be worth working out the wildcard thing to keep things simple

6:57 mikem`: ok, will try that after some food. thanks for the help so far

7: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)

7:50 Jomyoot: Can file a.clj (:use b) and b.clj (;use a)?

7:51 cark: nope

7:52 Jomyoot: seriously

7:53 hmmm

7: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

7:57 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)

7:57 (untested)

8:01 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

8: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)

8:23 durka42: try clojure.stacktrace/print-cause-trace

8:23 dysinger: I noticed clojure master on github has 'backtrace' too

8:23 (not 1.0)

8:26 maacl: That gives me java.lang.ClassNotFoundException: clojure.stacktrace (NO_SOURCE_FILE:0)

8:33 durka42: Should I do "clojure.stacktrace/print-cause-trace" at the Repl?

8:34 durka42: do (use 'clojure.stacktrace) first

8:34 clojurebot: clojure is like life: you make trade-offs

8:34 durka42: unless you don't have the latest clojure, in which case it might still be in clojure.contrib.stacktrace

8:36 maacl: durka42: ah, was in contrib

8:38 durka42: clojure.contrib.stacktrace/print-cause-trace -> #<stacktrace\$print_cause_trace__4400 clojure.contrib.stacktrace\$print_cause_trace__4400@5be2753e>

8:38 Chouser: whoa. I hadn't noticed that stacktrace came in with gtic

8:38 durka42: apparently

8:39 Chouser: that would explain that repl startup error I keep seeing but hadn't investigated yet...

8:39 durka42: :p

8:40 maacl: durka42: How is it supposed to be used ?

8:41 durka42: well, you cause an exception and then you can do (print-cause-trace *e)

8:50 maacl: durka42: does this have to be from the repl or can I do it from within a source file?

8:52 durka42: from within the source file it gives a nullpointer exception

8:57 durka42: you could catch the exception and run print-cause-trace on it

9:11 Jomyoot: i know there isn't for loop

9:11 but how would i efficiently emulate for loop. where i loop over 0..n

9:12 omg there is (loop)

9: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?

9:13 ,(for [i (range 10)] (+ i i))

9:13 clojurebot: (0 2 4 6 8 10 12 14 16 18)

9:13 Jomyoot: producing side effect

9:13 inserting into database

9:13 ChrisPS: ,(repeat 7)

9:13 clojurebot: Eval-in-box threw an exception:java.lang.OutOfMemoryError: Java heap space

9:13 Jomyoot: where I need to change index value from 0 to n

9:13 ChrisPS: ok

9:13 Chouser: ok, so you probably want doseq

9:14 (doseq [i (range 10)] (insert-into-db i))

9:14 that'll return nil after callking insert-into-db 10 times

9:15 actually, dotimes is even better

9:15 (dotimes [i 3] (prn i))

9:15 ,(dotimes [i 3] (prn i))

9:15 clojurebot: 0 1 2

9:17 Jomyoot: ,(range 20)

9:17 clojurebot: (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19)

9:18 Jomyoot: is there sin for using (for)?

9: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.

9:20 Jomyoot: i have to read into laziness

9:24 Chouser: ,(do (for [x [1 2 3]] (prn x)) "all done?")

9:24 clojurebot: "all done?"

9: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.)

9:25 ,(do (doseq [x [1 2 3]] (prn x)) "all done?")

9:25 clojurebot: "all done?"

9:25 1 2 3

9:25 jdz: ,(do (prn "foo") "done")

9:25 clojurebot: "done"

9:25 "foo"

9:25 Chouser: heh

9:27 jdz: clojurebot should prefix the lines depending on what kind of output it is (standard output/error, return value)

9:27 i'm not volunteering ;0

9:28 although i imagine the change would not be complicated

9: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 * artagnon looks for clojure-clr

10:00 jdz: artagnon: anyway, what's your point?

10:01 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 ~rationale

10:02 clojurebot: rationale is http://clojure.org/rationale

10:02 * artagnon is looking at clojure-clr and reading the 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 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 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 * artagnon nods

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 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 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 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 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 :-)

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 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: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 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 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 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 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 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 Chousuke: ofcourse, that's for testing.

10:27 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 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 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 I'll tell you which line, wait

10:32 Chousuke: hmm

10:32 maybe you have some old version of clojure-mode?

10:32 * artagnon looks

10:33 artagnon: I got it from jochu's GitHub head.

10:33 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 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 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 or did you mean stub somehow?

10:44 cemerick: There are times when one needs to produce a subclass, etc.

10:45 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 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 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 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 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 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 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 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 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:12 * artagnon is off to write code

11:13 * Raynes pops some popcorn and watches the ongoing discussion

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 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 consuming vs producing

11:20 in all likelihood newnew will retain proxy's ability to derive from concrete classes, and improve the super access story

11:20 but that's not the same as providing tools for defining abstract classes (other than existing genclass)

11:21 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 given newnew defines an anonymous class

11:22 cemerick: rhickey: well, see, I underestimated your ambitions again :-)

11:24 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 named

11:25 cemerick: Chouser: agreed.

11:25 rhickey: cuper is bad design

11:26 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 hm, actually, I make kind of mixin macro for proxy recently.

11:30 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 new anonymous class via proxy

11:31 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 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 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 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 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 (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 The template instance is just a handle that allows newnew to get a hold of the abstract base/mixin class.

11:51 (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 the tricks are: making newnew generate an abstract class when needed, constructors

11:57 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 cemerick: factory fn is not a ctor, can't be used to init a base

11:59 exposing the implicit ctor gets icky quickly

11:59 cemerick: right, right

12:00 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:01 * Chouser ducks

12:02 rhickey: e.g. (def x (let [a 42 b 57] (newnew [Object] [] ...))

12:03 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 Chouser: that abuse of final is in conflict with the optimizer - you might not see that new value in all threads

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 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 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 a.k.a. the holy grail

12:11 * cemerick waits for the punchline ;-)

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 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 assoc!, perhaps?

12:15 rhickey: cemerick: yes assoc!

12:16 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 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 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 I'm not sure bending newnew to do this is the right thing

12:20 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 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 express*

12:46 Chouser: ,(not= 2 3)

12:46 clojurebot: true

12:46 beutdeuce: k, thnx

12:50 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 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 (= nil "")

12:55 ,(= 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 no, i need to check if xs is not equal to zero

12:58 which is not=

12:58 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 ,(nil? ())

13:02 clojurebot: false

13:02 technomancy: ,(empty? ())

13:02 clojurebot: true

13:02 beutdeuce: ah

13:02 empty?

13:02 technomancy: beutdeuce: that's what you want instead

13:03 beutdeuce: ajd to check if it isnt empty?

13:03 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: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 technomancy: \m/

13:16 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 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 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 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 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 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 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: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 eep

14:22 thats not right

14:23 ~def clojure.lang.Delay

14:24 ~def pmap

14:30 hmmm

14:32 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:44 * ataggart looks up "reified"

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 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 nor did eliminating the delay

14:48 hiredman: weird

14:48 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 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 it seems like a good idea to check

14:50 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 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 my CA is in the mail

15:13 mebaran151: ataggart, that actually looks really nifty

15:13 ataggart: thx

15:14 needs better documentation and examples tho

15:15 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 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 e.g. slfj

15:24 whic I didnt get around to adding

15:26 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 (I've never seen it in the wild)

16:23 Chouser: I'd really like class name aliases

16:24 I guess this case is bad enough a macro would help...

16:29 cemerick: hiredman: and the winner goes to.....pmap + binding!

16:29 * cemerick screams, then whimpers.

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 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 (only in the UI, that is)

16:33 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 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 yields a fn

16:37 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 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 and pcalls, and future, and send

16:40 Raynes: technomancy: Happens here on Ubuntu Hardy as well.

16:40 Apparently Brian Carper gets the same thing.

16:41 Chouser: does dosync use thread-local data?

16:41 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 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 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 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 not normally what you want wrt dynamic vars, but perhaps explicitly

16:47 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 (pmap #capture-this-env(foo %) xx)

16:49 people really expect that of the anonymous fns they pass to pmap, but not in their defns

16:49 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 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 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 fn-capturing

16:54 (fn-capturing [vars] [args] body)

16:54 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 the bindings system is designed to make that possible, or at least was

16:56 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 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 ;-)

17:00 no, my current/former plan is just wrongheaded.

17:03 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:12 this will let you build the (preferable) capture-all-bindings

17:13 {#'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 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 wants binding*.

17:15 kotarak: and

17:16 * kotarak wonders what happens if body contains "(finally something...)".

17:16 rhickey: binding* ?

17:16 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 rhickey: this is what I had in mind: http://paste.pocoo.org/show/129042

17:22 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 no user should be writing finally outside of try

17:23 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 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 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:57 * Chouser writes (dorun (map ...))

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 just checking. :-)

18:02 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 ah, I misunderstood the api!

18:03 * Chouser deletes (dorun (map ...))

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 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 didn't walk move to Clojure itself when c.c.test-is did?

18:48 that might be the cause of your breakage. walk is no longer in contrib.

18:48 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 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 aaand it works! thanks!

19:55 rhickey: hmm, PersistentVector in batch mode slightly faster than j.u.ArrayList!

19:56 sorry, Vector

19:57 only slightly slow than ArrayList

19:59 slower

20:01 schaefer_: i'm curious why the following doesn't work: (def (with-meta (symbol (str 't)) {:t "t"}) "t")

20:02 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: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 #'user/foo

20:10 user=> ^#'foo

20:10 {: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 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 user=> (def #^{:when (java.util.Date.)} foo 42)

20:13 #'user/foo

20:13 user=> (meta (var foo))

20:13 {: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 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 just found it weird that name didn't work on it

21:08 Chouser: yeah. me too.

21:08 of course not all vars have a name

21:08 arohner: oh, right

21:08 rhickey: ArrayList

21:08 time: 1129

21:08 PersistentVector (batch mode)

21:08 time: 1354

21:08 smokin!

21:08 arohner: with-local-vars, etc

21:10 rhickey: was:

21:10 PersistentVector

21:10 time: 3547

21:11 Chouser: wow

21:12 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 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 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 just added a bit for editing

21:15 I'm still stunned at how fast the tree structure is

21:18 those times are for a million writes and 10 million reads of a 100k item arraylist/pvector

21:18 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 most readable things are small, its vec/vector/into that are going to blaze now

21:20 and the mutable mode will be available for end user code as well

21:22 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 er, protect immutability

21:23 Chouser: I really can't imagine it'll be a problem

21:23 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 right

21:24 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 :)

21:26 Chouser: heh. I don't doubt it.

21:26 rhickey: revisiting it was not fun

21:26 Chouser: oh!

21:26 you mean the way it was previously was gnarly?

21:26 rhickey: yeah

21:27 lots of bit shifts etc

21:27 Chouser: ah, yes. I know that very well.

21:27 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 also it grows up, not down

21:31 in any case, these perf tests show it will be well worth it, no compromises in using Clojure's data structures

21:32 I think the technique will generalize to any tree

21:32 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 that was my target

21:41 but the question becomes, once you have a bottleneck, do you have to leave everything good behind?

21:42 Chouser: yeah, that's similar to your response at the meetup to my all-too-frequent joke about speed.

21:42 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 the latter is the toughest nut to crack

21:43 Chouser: arrays are going to be ... yeah

21:44 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 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 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 ...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 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 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 it's a toy

22:26 Chouser: I've thought things like that before ... though I wanted order-preserving maps.

22:26 ...and that was before I knew lisp or what homoiconicity was.

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 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 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 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 lisppaste8: url?

22:44 lisppaste8: To use the lisppaste bot, visit http://paste.lisp.org/new/clojure and enter your paste.

22:45 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 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 but you do the same thing for both

22:47 mebaran151: right now yes, but I'll handle them differently later

22:47 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 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 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 you pass it a hierarchy

22:49 which it walks just like it would walk the Classes

22:50 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 how to get key of value in a map?

23:40 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"

Logging service provided by n01se.net