#clojure log - Feb 27 2009

The Joy of Clojure
Main Clojure site
Google Group
List of all logged dates

0:13 erohtar: chouser: you there?

2:24 hiredman: my sentiments exactly

2:24 clojurebot: translate from jp #?4?????????,?????????????????????????

2:26 clojurebot: translate from ja #?4?????????,?????????????????????????

2:26 clojurebot: # 4 I'd like to get one, and it would be prudent to think out collection

2:27 hiredman: clojurebot: translate from ja ?????

2:27 clojurebot: Now

3:50 Lau_of_DK: Hey guys

3:53 cmvkk: i have these two concurrent functions, one is hand rolled and the other uses pmap. they both run at almost exactly the same speed (one's 4% slower), but only the pmap version pegs both cores.

3:53 why wouldn't (doseq [cur-agent list-of-agents] (send-off cur-agent a-long-function)) run concurrently?

4:16 Lau_of_DK: (doc send-off)

4:16 clojurebot: Dispatch a potentially blocking action to an agent. Returns the agent immediately. Subsequently, in a separate thread, the state of the agent will be set to the value of: (apply action-fn state-of-agent args); arglists ([a f & args])

4:50 kotarak: The docstring discussion will also never die.......

4:51 cmvkk: it appears that i'm send-offing 4 agents at once, but three of them always wait for the first one to finish.

4:59 aha! i was using an incrementing atom (swap! counter inc) to make sure they didn't step on each other's toes, but apparently you can't do that from simultaneous dispatches.

5:03 Lau_of_DK: Try commute instead

5:03 For a counter it'll work just fine

5:07 cmvkk: actually i really only needed to assign each agent a number then increment by (+ cur-num *num-agents*).

5:07 one of those 'should have used a pencil' scenarios...

5:12 Lau_of_DK: k

6:12 djpowell: Hmm, strings are seqable, but stringbuffers aren't. It might make more sense to define StringSeq to work over any CharSequence

6:13 leafw: djpowell: time to submit an enhancement proposal.

6:25 these tweets are out of place here, IMO.

6:27 Holcxjo: I agree -- maybe we need a #clojure-social channel for these bulk messages?

6:31 leafw: Holcxjo: do you know who is in charge of the clojurebot? Just suggest it to him if you do.

6:32 Holcxjo: Nope.

6:32 clojurebot: who is your master?

6:32 clojurebot: Gabh mo leithsc�al?

6:32 Holcxjo: :-)

6:37 Hun: clojurebot: take me to your leader

6:37 clojurebot: Huh?

6:40 leafw: clojurebot: bdfl

6:40 clojurebot: I don't understand.

6:52 leafw: ,(doc find-doc)

6:52 clojurebot: "([re-string-or-pattern]); Prints documentation for any var whose documentation or name contains a match for re-string-or-pattern"

7:30 AWizzArd: clojurebot: max people

7:30 clojurebot: max people is 149

7:35 kotarak_: leafw: isn't it hiredman?

7:40 leafw: kotarak_: sounds familiar; perhaps yes

8:05 clojurebot: svn rev 1311; fix print-method for objects with :type meta but no print-method, patch from Konrad Hinsen

8:39 MarkVolkmann: Is it correct to say that each loaded function is represented by an AFn object in memory?

8:55 kotarak_: MarkVolkmann: I think macros are simply functions with :macro true in their metadata.

8:55 Correction: Vars

8:56 You can even do (defmulti bla ....) and then a (.setMacro #'bla).

8:56 And you get a multimacro.

10:30 Holcxjo: Is there a form of let where the value can already make use of the name to be defined? I.e. something so that (let [a (cons 1 (map inc a))] (take 4 a)) works?

10:31 Or do I have to model this with a loop or something?

10:41 Chouser: Holcxjo: interesting. the most common need for that is a fn referring to itself from inside its body. that's supported.

10:41 otherwise you'll need some kind of reference type, a global var being the most common.

10:42 If you don't like the global, you can use with-local-vars to get a local var, or use an atom or ref stored in a local.

10:44 Holcxjo: Chouser: I don't like the idea of an atom or ref or anything -- this is a fully functional algorithm...

10:44 I'll need to learn about with-local-vars

10:45 Chouser: or use a different construct for the body of the expression. 'iterate' would work in your (I assume simplified) example.

10:46 ,(let [a (atom nil)] (reset! a (lazy-seq (cons 1 (map inc @a)))) (take 4 @a))

10:46 clojurebot: (1 2 3 4)

10:48 rhickey: looks like someone wants letrec again

10:49 Holcxjo: :-) Pretty please?

10:50 rhickey: Chouser: looked at :let, wondering if there might be some subtleties about order now :when guard :let [x something-expensive]

10:50 Chouser: rhickey: yes

10:50 :-)

10:50 rhickey: :when and :while order themselves

10:50 looks like :let always first in your patch?

10:51 Chouser: Is it really impossible for someone to want to use :when to guard and expensive :while?

10:51 yes, :let comes first in my patch.

10:51 "an expensive"

10:52 Holcxjo: Chouse: Couldn't that possibly cause exceptions? for [x natural-number :when (pos? x) :let [inv (/ 1 x)]

10:52 I.e. the let may rely on the guard to be true to be valid

10:53 Chouser: Holcxjo: I think that's what rhickey is noting, yes.

10:53 Holcxjo: Chouser: Well he said "expensive". I meant "failing"

10:54 Chouser: but I wasn't sure if that was unacceptible, since it seems possible that the existing :when and :while have a similar ordering issue.

10:58 (for [a (range -2 5) :when (not= a 0) :while (> (Math/abs (/ 1.0 a)) 1/3)] a)

10:58 or something.

10:59 I imagine allowing :when and :while to guard each other and :let would be possible, but would take a bit of work.

11:10 Holcxjo: So, back to my problem :-)

11:10 How does one canonically and concicely represent a problem where I want to do something like (let [result (cons start-value (map some-function result other-seq1 ...))] result)

11:10 Do I need to pollute the namespace with a global name to do that?

11:12 Chouser: did I fail to answer that? I think I listed the possible routes to solving that.

11:13 Holcxjo: Sorry, so the canonical way if via an atom? Yikes. :-(

11:15 My apologies -- I assumes that was just a quick hack while thinking about a cleaner solution... I'll give that a try I guess

11:15 Chouser: well, your options are: global var, local reference type (var, atom, ref), or adjust your expression: somehow use a named fn or use something that can refer to itself but not by name

11:16 for the example you posted, 'iterate' would work. It might not in your real world case, but perhaps something similar could be done.

11:17 if nothing like that works, perhaps a self-recursive function would work, since they can have a private name for their own body:

11:17 ,(take 5 ((fn foo [] (lazy-seq (cons 1 (foo))))))

11:17 clojurebot: (1 1 1 1 1)

11:18 Chouser: failing that, and not wanting to touch a namespace global, 'atom' is probably the simplest reference type.

11:18 kotarak: Isn't that a candidate for a lazy-seq? (let [make-seq (fn make-seq [start-value other-seq] (lazy-seq (cons start-value (make-seq (compute-new start-value (first other-seq)) (rest other-seq))))) a (make-seq start-value other-seq)] (take 4 a)), not tested and kotarak is absolutely unclear about the new lazy-seq stuff.

11:18 Chouser: note it's mutated exactly once, so it's not as if the mutation is getting into your algorithm.

11:19 Holcxjo: Chouser: Thanks. That was a very elaborate an complete answer

11:19 Chouser: Holcxjo: I realize it's not letrec, but it's what we've got right now

11:19 Holcxjo: So, is there a reason for the absence of letrec?

11:20 Chouser: you'd have to take that up with rhickey. it's not something I've thought about, but I'm sure he has.

11:21 kotarak: possibly something like that, yes, leveraging the private name feature of fn.

11:22 rhickey: http://code.google.com/p/clojure/issues/detail?id=89

11:24 Chouser: rhickey: would it not be called 'let'?

11:24 Holcxjo: rhickey: Thanks. I added my star to that

11:24 Chouser: I guess that would prevent using let to repeatedly replace the same local name.

11:25 kotarak: argh :(

11:25 rhickey: Chouser: no

11:33 Where is Holcxjo's original problem? (came in late)

11:36 Chouser: (let [a (cons 1 (map inc a))] (take 4 a))

11:36 rhickey: Chouser: thanks

11:38 would become: (letrec [a (lazy-seq (cons 1 (map inc a)))]

11:38 Chouser: and not 'let' because it would be a breaking change?

11:40 Holcxjo: rhickey: (let [result (cons start-value (map some-function result other-seq1 other-seq2 ...))] result)

11:40 So basically a simple iterate doesn't work as I need other sequences as well in there

11:41 rhickey: mixing let and letrec is not normally done in non-lazy languages letrec/labels/let rec(ML)

11:43 e.g. simply (letrec [a (cons 1 (map inc a))] (take 4 a)) wouldn't work because a is undefined when used in the initialization call to map

11:44 kotarak: ,(let [make-seq (fn make-seq [start-value] (lazy-seq (cons start-value (make-seq (inc start-value))))) a (make-seq 1)] (take 4 a))

11:44 clojurebot: (1 2 3 4)

11:45 kotarak: Holcxjo: so lazy-seq may help you now, until letrec is available.

11:45 Holcxjo: kotarak: Thanks -- need to look into that to understand this solution

11:47 kotarak: Holcxjo: see also my previous example how to incorporate other-seq etc.

11:54 Holcxjo: kotarak: Thanks: I missed that example

11:55 kotarak: Holcxjo: it's kind of manual map

11:59 drewr: rhickey: Is (if (first s) s nil) an acceptable seq/nil implementation? That's essentially the patch I submitted.

12:03 Chouser: drewr: which patch is that, if I may ask?

12:04 drewr: Chouser: http://code.google.com/p/clojure/issues/detail?id=87

12:04 Chouser: ah, searching for "drew" was getting me nowhere.

12:04 :-)

12:05 drewr: I'm surprised it doesn't put a name in there for gmail addresses.

12:07 Chouser: I think (if (seq s) s nil) or perhaps just (seq s) might be more generally correct, but I'm not sure if there's a specific problem with what you've got.

12:08 drewr: Good call. (seq s) is probably better.

12:08 brb

12:14 cgrand: Holcxjo: you can hide the atom trick into a macro, that's what seq-utils/rec-seq does: (let [a (seq-utils/rec-seq a (cons 1 (map inc a)))] (take 4 a))

12:19 Mec: how come let uses a vector to represent bindings instead of a map? is it just the aesthetic of the square vs curley or is there a reason?

12:21 kotarak: Mec: locals are serial: (let [a (initial-step) b (next-step a) c (another-step a b)] (use a b c here)). You loose this with a map.

12:24 Holcxjo: cgrand: Thanks -- that looks neater; I'll try that

12:24 Mec: kotarak: makes sense i guess, thanks

12:36 drewr: Looks like seq is slightly faster than first too.

13:15 clojurebot: svn rev 1312; updated add-watch doc

13:26 erohtar: chouser: u there?

13:26 Chouser: yep

13:26 erohtar: chouser: so i was thinking about our conversation yesterday about vars

13:27 chouser: so vars in clojure arent the same as dynamically scoped variables in common lisp?

13:27 Chouser: I don't know CL well enough to say for sure, but my understanding is that they're pretty much equivalent.

13:28 a possible difference being how threads interact with them -- not sure what CL does there.

13:28 erohtar: chouser: the thing is... in common lisp, if i bing var-a to x, any thing down the call stack will see x...

13:30 chouser: in clojure, it doesnt seem to behave like a closure... (laziness, during realization) is that correct? if var-a was bound to y elsewhere, realization of the old lazy stuff would now see y

13:30 chouser: is that right?

13:31 Chouser: CL doesn't do lazy seqs, right? so lets ignore that for now

13:31 erohtar: chouser: ok...

13:31 Chouser: let's just look at how a closure works.

13:32 gnuvince: Is there a better tutorial about Swing than the Trail on on Sun's site?

13:32 erohtar: chouser: i think i understand how a closure works, but when a closure is going to be evaluated lazily... what happens to a var that has since been rebound?

13:33 Chouser: (def var-a 2), (let [add (binding [var-a 5] (fn [n] (+ var-a n)))] (add 3))

13:33 ok, here's a little example that I would expect to work the same if translated to CL

13:34 var-a has a root binding of 2.

13:34 erohtar: chouser: that should eval to 8, yes?

13:34 Chouser: no

13:34 erohtar: chouser: hmmm

13:34 Chouser: within the dynamic scope of the binding, a closure is created that refers to var-a

13:35 erohtar: chouser: ok... and that is 5?

13:35 Chousuke: nah, it's var-a

13:35 erohtar: oh

13:35 Chouser: at the moment the closure is created, var-a has a thread-local binding of 5, but that doesn't really matter because nothing is asking for var-a's *value*, just grabbing var-a itself.

13:36 erohtar: i see... ok...

13:36 Chouser: so that closure is returned to outside the binding, and named 'add'

13:37 Chousuke: I guess it's a bit surprising though.

13:37 Chouser: and what does the closure have bundled inside? it knows it wants one arg n, and it has references to the vars var-a and +

13:37 erohtar: and it dynamically uses the value 2, for var-a when it needs it?

13:37 Chouser: right

13:37 erohtar: arent closures supposed to capture their environment?

13:38 Chouser: their lexical environment, yes.

13:38 erohtar: not the values?

13:39 Chouser: which is why this works: (def var-a 2), (let [m 7, add (binding [var-a 5] (fn [] (+ var-a m)))] (add))

13:39 hm, not that's not quite right, since m is still available when 'add' is called. But anyway, it wouldn't have to be.

13:40 erohtar: thinking about this

13:40 Chouser: if you think about how dynamic vars are used in clojure.core, you'll see it must be this way.

13:40 let's look at *in*

13:40 er, *out*

13:40 Chousuke: ,(let [x 1 add (fn [] (+ 4 x))] [(binding [x 2] (add)) (binding [+ -] (add))])

13:40 clojurebot: java.lang.Exception: Unable to resolve var: x in this context

13:40 Chousuke: hmm

13:41 erohtar: ok - talking about *out* - is that the std-out ?

13:41 Chouser: somewhere there's something like (def *out* System/out)

13:41 right

13:41 Chousuke: ,(let [x 1] (let [add (fn [] (+ 4 x))] [(binding [x 2] (add)) (binding [+ -] (add))]))

13:41 erohtar: right

13:41 clojurebot: java.lang.Exception: Unable to resolve var: x in this context

13:41 Chouser: that's the root binding of *out*

13:42 erohtar: ok, with u so far

13:42 Chousuke: hmm, I guess binding let-bound variables does not work

13:42 Chouser: then you've got functions like prn that use *out*

13:42 erohtar: yes... i think they need to be vars

13:42 right

13:42 kotarak: let-bound locals are not Vars

13:43 Chousuke: hmm

13:43 ,(let [x 1] #'x)

13:43 clojurebot: java.lang.Exception: Unable to resolve var: x in this context

13:43 Chouser: we can pretend prn is defined something like (defn prn [& args] (.write *out* args))

13:43 erohtar: when u create a lazy something, that uses prn, what happens?

13:43 Chousuke: indeed.

13:44 Chouser: now, if prn took the value of the *out* when it was defined, there would be no way to get prn to write to any other stream -- it would always use the root binding, since that's what was in play when prn was defined.

13:44 but that's not how it works.

13:44 prn has a reference to the Var *out*, not its value.

13:44 erohtar: so something like - (map #(prn %) [1 2 3])

13:44 chouser: i completely agree

13:45 Chouser: so instead, prn will use the dynamic value of *out* when you call it.

13:45 erohtar: chouser: hmmm

13:45 Chouser: ,(with-out-str (prn 5 10))

13:45 clojurebot: "5 10\n"

13:45 Chouser: got it?

13:45 erohtar: i do

13:45 im thinking about my confusion - and trying to figure out the question to ask next

13:46 so lets take my example -

13:47 (map #(prn %) [1 2 3])

13:47 now, somewhere else, i rebind *out* to str... and in there, if i use the above object, will it also use the new binding?

13:47 it will...

13:48 i think im getting it now

13:49 ok - final thought -

13:49 Chouser: (def q (map #(prn %) [1 2 3])), (with-out-str (doall q))

13:50 so yes, even though you created the lazy seq and closure outside the with-out-str, since its not computed or realized until the doall which is within the with-out-str, all those prn's will use the binding provided by with-out-str

13:50 erohtar: got it...

13:50 so my final thought - what if instead of *out* we're talking about a connection to a database... bad use of vars, right?

13:51 Chouser: now lazy seqs can be a bit tricky because they cache their results and can effectively contain multiple closures.

13:51 hmm....

13:51 erohtar: if i read from a db a list of objects using a var that holds database config.... and then i rebind the config to write into another database

13:51 Chouser: if you're going to open a connection to a database, do a batch of work in a single thread on it, and then close the connection -- then that would be a fine use.

13:52 erohtar: since the initial read may be lazy... when it realizes... it will 'read from the wrong place'"?

13:52 Chouser: right

13:53 basically you want to be very careful about passing closures (and therefore lazy seqs) past 'binding' boundaries.

13:53 erohtar: chouser: on that thought - what i did was, i read from a db, and to write into the other, i created agents, binding the new db config in each

13:53 chouser: it didnt work...

13:54 chouser: ok... im beginning to understand ...

13:54 i need to rethink what im doing

13:54 and avoid using vars for some of this stuff, since im doing a lot of agent related stuff

13:55 Chouser: yes, I'd recommend trying to have the behavior of as much of your code as possible depend only on the args passed directly in.

13:55 erohtar: chouser: this whole mess started when i wanted to avoid passing db-config around everywhere

13:55 Chouser: this is better for testing, reasoning about behavior, easier to work with threads, closures, laziness, etc.

13:55 heh, yeah.

13:56 erohtar: yup - i hear you

13:56 Chouser: well, another thing that may be helpful -- just guessing since I haven't seen your code...

13:56 erohtar: thanks a ton for your time - ur incredibly helpful

13:56 ok?

13:56 Chouser: would be to try to keep as much of your code purely functional as possible.

13:56 erohtar: yes - i understand

13:57 Chouser: so avoid writing a function that takes some args, does some computation, and then writes to a db. Instead, have a fn that takes args, does computation, returns result.

13:57 then perhaps you don't have to pass in db config at all -- whoever calls this fn can to the db write itself.

13:57 erohtar: i think it mostly is... except for this stuff... where i ended up inadvertently depending on "global" db-config

13:57 well, this is a persistence layer

13:58 i take an object in, break it up etc., and put it into hbase

13:58 the code is open-sourece... would u like to see?

13:58 Chouser: hm, sure. I may need to ramp up on hbase soon.

13:58 erohtar: http://github.com/amitrathore/capjure/blob/b939a7038ebb4aed0d068d6e86291cc881ecf72b/src/org/rathore/amit/capjure.clj

13:59 its kind a messy - learning clojure and hbase while doing this...

13:59 Chouser: :-)

14:00 Chousuke: erohtar: first thought: use some newlines.

14:00 danlarkin: Noooooooooooo -jure

14:00 erohtar: danlarkin: haha - u've told me that already

14:00 Chouser: danlarkin: hey, at least he's got a name

14:00 Chousuke: erohtar: one newline after each def/defn would make it a lot nicer :)

14:00 danlarkin: erohtar, Chouser: :)

14:01 erohtar: ok - more newlines, check

14:01 Chousuke: well, the def group is fine I guess.

14:02 but (defn foo) (defn bar) without an empty line in the middle is a bit weird

14:02 erohtar: ok - those were sort of just quick utility functions... but i'll put newlines

14:03 Chousuke: newlines never hurt anyone

14:03 Chouser: erohtar: this is what you get for posting code. unsolicited advice. :-)

14:03 erohtar: haha

14:03 its fine - maybe i'll learn something too :)

14:03 Chouser: but while we're at it -- I much prefer to have the code arranged so that 'declare' is only needed in mutually-recusive situations.

14:04 though others may disagree with me

14:04 erohtar: right - i struggled with that one - cause i dont like to have to order the functions in "reverse"

14:04 * Chouser nods

14:05 erohtar: see - capjure-insert is the only one that most people will call

14:05 Chousuke: erohtar: you could add more newlines and some kind of "headlines" for sections of code

14:05 erohtar: u bind the config stuff - *hbase-master* and *primary-keys-config* - and then u call capjure-insert with the params

14:05 Chousuke: so people can easily skip the helper function blocks

14:05 erohtar: most of the remaining functions are functional

14:06 only the ones dealing with hbase have (obvious) side-effects

14:06 and it works fine... and its in production too... ran into issues trying to use TWO hbase configs (to move data between them)

14:06 Chousuke: that's a lot of functions for one namespace too, though :)

14:07 Chouser: I really don't think more namespaces would be better.

14:07 Chousuke: if possible maybe it'd make sense to put the "side-effecting" functions (that deal with hbase) in a separate file or namespace?

14:07 erohtar: cause lazy lists of data from one hbase, seem to be getting messed up when i rebind the config to start writing into the other hbase

14:08 do u know what i mean? (trying to get discussion back to lazy closures and bindings :) )

14:08 Chousuke: ah

14:08 right, I can see why that happens :)

14:08 the laziness gets evaluated in the context of the new hbase.

14:08 erohtar: well, im only starting to see it :) thanks to u guys...

14:08 yea

14:08 drove me crazy

14:09 and i did all this only so i wouldnt need to pass the db config around all the functions

14:09 like u said, there are a lot of them...

14:09 Chousuke: which means you will have to move away from using global vars for config and instead create something you explicitly pass around to the functions.

14:09 erohtar: so now what... ? pass the config around everywhere?

14:10 Chousuke: maybe only to a few core functions.

14:10 erohtar: yea

14:10 Chousuke: which then would bind the dynamic vars.

14:11 erohtar: well, if i pass it in, i dont need dynamic vars, rite

14:11 Chouser: or don't use lazy seqs

14:11 use doall or stuff them in a vector first

14:11 erohtar: hmmm

14:11 cemerick: uk, the ILC registration site doesn't allow one to register more than one person at a time

14:11 erohtar: that might be one solution

14:11 Chousuke: right, if they are side-effecting that might be better.

14:12 Chouser: cemerick: how many people are you?

14:12 Chousuke: though maybe not memory-efficient :/

14:12 erohtar: yea

14:12 cemerick: Chouser: oddly enough, 2 ;-)

14:12 Chouser: :-)

14:12 erohtar: i think i will get rid of the dynamic vars

14:12 Chousuke: it's a shame though

14:12 erohtar: what is the pattern for this kind of 'static' config being passed around?

14:12 Chousuke: they make the code much easier to write :P

14:13 cemerick: I may end up sticking around through Tuesday -- Sussman's talk on Tuesday sounds fantastic

14:13 Chousuke: I wonder if there's a better solution

14:13 some way to "bind" the current config to when the lazy-seq is first created

14:13 erohtar: in typical OO, u can create an instance variable to hold the config - i wonder if in clojure u can create something like that... and since its immutable, it should be fine, rite?

14:14 Chouser: yes

14:14 Chousuke: maybe do (let [config *global*] (return-some-lazy-seq-qith config))?

14:14 erohtar: but there is no way to do that, rite?

14:14 Chousuke: would that fix the value?

14:14 erohtar: yea

14:14 Chouser: stuff all your config into a map, and pass that single thing into functions that need to use it

14:14 erohtar: i thought of doing something like that... but other functions couldnt access those values...

14:15 i thought of simulating an 'immutable object oriented system' -

14:15 like how u do in javascript... but immutable

14:15 Chousuke: I did in clojurebot so that I pass a map of config stuff around with every function

14:15 erohtar: return a closure with the vars bound with a let... and then closures for all the other functions also

14:16 Chousuke: it's a bit tedious though :/

14:16 erohtar: i dunno if im being clear

14:16 very tedious

14:16 macros can help

14:16 essentially what id be doing is creating an immutable OO system

14:16 something like that

14:16 does that make sense?

14:16 Chousuke: erohtar: but your problem is only with lazy seqs right?

14:16 erohtar: lazy seqs and dynamic vars

14:17 Chousuke: erohtar: don't you only need to fixate the config when returning a seq

14:17 erohtar: the interplay

14:17 yes

14:17 Chouser: erohtar: clojure.zip does something like that

14:17 erohtar: chouser: like what?

14:18 Chouser: each node is a object with a several functions attached as metadata

14:18 erohtar: chouser: ah, i see

14:18 got it

14:18 Chouser: most of the api fns are like (children my-node), where children calls fns from my-node's metadata, passing in my-node

14:19 erohtar: i see -

14:19 thats rather cool - so u can basically have my-node equivalent contain the config data

14:20 Chouser: sure

14:20 erohtar: alright folks, thanks a lot for this conversation - im going to experiment and see how to get this done with the least amount of work :D

14:20 Chouser: the only reason zip nodes have fns in metadata is so it can be polymorphic.

14:20 erohtar: yes- makes sense

14:21 (then everyone can tell me how my code sucks :) )

14:21 Chouser: if you're going to only have one kind of db config (seems likely) then you don't need the metadata piece.

14:21 erohtar: yes - just need to take care of the fact that the config will be rebound... so just closures that capture the config, and the functions that use it

14:21 something like that

14:41 durka42: oh dear

14:41 that post on ACP must be before lazy

14:41 it's confusing me because it calls rest and has parameters named next

14:42 Chousuke: heh

14:42 I wonder what the point of the runnable function is

14:43 did the author forget that clojure fns implement runnable

14:43 the use of cons seems weird too :)

14:44 maybe it makes more sense to someone with a CL background, but I'd just use a literal vector :P

14:45 kotarak: literals have their gotchas, too. ;)

14:45 Chousuke: I guess

14:46 but (apply foo (cons bar zonk)) just looks unclojurey :)

14:49 kotarak: oerk

14:50 * kotarak almost never used cons in Clojure up to now....

14:50 Chousuke: yeah, it's pretty rare

14:51 rhickey: (apply foo (cons bar zonk)) ==> (apply foo bar zonk)

14:52 Chousuke: oh, right.

14:52 no need for a vector either.

14:53 kotarak: well, cons will be revived with lazy-seq, I fancy.

14:56 the author probably doesn't know that between the sequence and the function in apply, there might be other arguments. I came across this misunderstanding several times now.

14:58 tashafa: ,(. "test" (replaceAll #"e" "a"))

14:58 clojurebot: java.lang.ClassCastException: java.util.regex.Pattern cannot be cast to java.lang.String

14:58 tashafa: hmm.. what im i doing wrong?

14:58 ,(. "test" (replace "e" "a"))

14:58 clojurebot: "tast"

14:59 tashafa: ,(. "test" (replaceAll "e" "a"))

14:59 clojurebot: "tast"

14:59 tashafa: ,(. "test" (replaceAll #"e|s" "a"))

14:59 clojurebot: java.lang.ClassCastException: java.util.regex.Pattern cannot be cast to java.lang.String

15:00 Mec: anyone know the etymology of clojure

15:00 tashafa: doesn't "replaceAll " take a regexp pattern as its first argument?

15:00 Chouser: tashafa: no, it takes a string

15:01 kotarak: ,(replaceAll "test" "e|s" "a")

15:01 clojurebot: java.lang.Exception: Unable to resolve symbol: replaceAll in this context

15:01 kotarak: ,(.replaceAll "test" "e|s" "a")

15:01 clojurebot: "taat"

15:01 Chouser: so sad

15:01 ,(.replaceAll "test" (str #"e|s") "a")

15:01 kotarak: sad?

15:01 clojurebot: "taat"

15:02 MarkVolkmann: What's the best/most idiomatic way to return a non-lazy map from a function when that function obtains a lazy map by calling some other function?

15:02 This is related to some database code.

15:02 Chouser: so sad that .replaceAll takes a regex as a string instead of as a regex.

15:02 kotarak: Chouser: the glories of interface design....

15:02 tashafa: ah ...thanks guys

15:03 MarkVolkmann: The result set is a lazy map. I want to return it, but when this function exits, the ResultSet will be closed and I won't be able to get to the ResultSet anymore.

15:03 kotarak: MarkVolkmann: I suppose you mean (map ....) by "map". I would thing doall.

15:03 Chouser: "lazy map" isn't the right term. map returns a lazy seq

15:03 tashafa: ,(. "test" (replaceAll "e|s" "a"))

15:03 clojurebot: "taat"

15:03 MarkVolkmann: Oh right ... a lazy seq of MapEntry object.

15:03 kotarak: There is a lazy-map though. *commercialonair*

15:04 Chouser: a seq of mapentry objects? you want to put them in a hash-map perhaps?

15:05 in general you can use doall to realize a lazy seq so it's no longer lazy, or use vec to create a non-lazy vector.

15:05 MarkVolkmann: Got it! Returning (doall rs) did the trick.

15:06 Chouser: Mec: http://groups.google.com/group/clojure/msg/766b75baa7987850

15:11 Mec: ah hah, thanks. i'd guess that clojure has closures then?

15:11 Chouser: indeed it does

15:26 MarkVolkmann: I think you have to get to the point that you're convinced about the benefit of code and data having the same syntax.

15:26 Someone posted the URL of an article that addresses this. Just read it last night.

15:27 http://www.defmacro.org/ramblings/lisp.html

15:27 pjstadig: http://paul.stadig.name/2009/02/clojure-terracotta-yeah-baby.html

15:28 MarkVolkmann: It supports the use of parens in Lisp by stressing the advantages of being able to easily write code that generates code.

15:28 pjstadig: i wrote up my experiment with integrating Clojure and Terracotta

15:28 rhickey: pjstadig: just read it - fun!

15:30 pjstadig: yeah it was fun

15:30 thanks for a great language

15:33 rhickey: pjstadig: you're welcome!

15:35 Mec: is a trailing . a special form or a macro?

15:39 kotarak: Mec: A trailing to dot is a synonym for new: (some.Class.) <=> (new some.Class)

15:39 Mec: leading dot is for methods: (.method some-object) <=> (. some-object method)

15:39 Mec: and / is for static members: some.Class/STATIC_CONSTANT

15:40 ,(macroexpand-1 '(String. "Hello"))

15:40 clojurebot: (new String "Hello")

15:40 WizardofWestmarc: and don't forget the ever fun $ for internal classes

15:40 had fun w/that last night before Chouser reminded me to use it

15:41 "why the hell can't I access this enum?" "oh just do *blahblah full qualified path*.classname$subclassname/enumval

15:41 *lights go on and everything just works*

15:44 kotarak: Also Class. and .method are treated specially in syntax-quote.

15:44 ,`(String.)

15:44 clojurebot: (java.lang.String.)

15:44 kotarak: ,`(.method obj)

15:44 clojurebot: (.method sandbox/obj)

15:44 cp2: i have a string that i want to coerce into a map. its formatted as "key\value\key\value....". obviously, i split by \ to get each component. whats a quick/decent way to use the evenly indexed elements in the resulting array as the keys parameter for zipmap, and likewise with the oddly indexed values?

15:45 kotarak: cp2: why not simply (apply hash-map splitted-things)?

15:45 leafw: cp2: get even into one list, odd into another, then zipmap

15:45 cp2: leafw yes, i know, but i was wondering if there was a better way

15:45 kotarak ok, ill see

15:46 hiredman: ,(apply hash-map (.split "foo\bar\bleep\bloop" "\"))

15:46 clojurebot: EOF while reading string

15:46 cp2: they need to be escaped

15:46 \\

15:46 hiredman: ,(apply hash-map (.split "foo\bar\\bleep\\bloop" "\\"))

15:46 clojurebot: java.util.regex.PatternSyntaxException: Unexpected internal error near index 1 \ ^

15:46 cp2: yeah

15:46 i got the same thing

15:46 hiredman: ,(apply hash-map (.split "foo\bar\\bleep\\bloop" "\\\\"))

15:46 clojurebot: java.lang.IllegalArgumentException: No value supplied for key: bloop

15:46 hiredman: anyway

15:47 kotarak: ,(apply hash-map (.split "foo\\bar\\bleep\\bloop" "\\\\"))

15:47 clojurebot: {"foo" "bar", "bleep" "bloop"}

15:47 cp2: oh right

15:47 regexps are funky in java

15:47 double escaping, etc

15:47 thanks

15:47 leafw: cool -- learnt something :)

15:49 hiredman: nah, the funkiness is because those are not actaully regexs

15:49 those are strings that get turned into regexs

15:49 cp2: hiredman, String.split actually does take a regexp

15:49 well, yes

15:49 you are right, i should have let you finish typing :)

15:56 ah, one more bit onto my previous question

15:57 actually, nevermind

15:59 cemerick: pjstadig: nice writeup

16:00 pjstadig: thanks

16:00 WizardofWestmarc: haven't had a chance to read it but I plan to later. Terracota looks interesting as hell, and mixed with Clojure? Mmmm

16:01 pjstadig: yeah i think its a really compelling combo

16:01 and of course the only production clojure app (that I know of) supposedly uses Terracotta

16:02 WizardofWestmarc: *nods*

16:02 It's stuff like Terracotta, and on my end Smack, which really make me appreciate the choice of putting Clojure on the JVM

16:02 pjstadig: it was definitely a brilliant move

16:03 gnuvince: WizardofWestmarc: Smack?

16:03 Clojure made me like the JVM

16:03 cemerick: is the appeal of Terracotta the semi-unlimited memory potential?

16:03 gnuvince: Now I just need to learn how to use Swing :-/

16:03 WizardofWestmarc: gnuvince: It's an xmpp client library

16:04 cemerick: vs. messaging-oriented architectures, I should say

16:04 WizardofWestmarc: setting up a web app w/processing backend written in clojure

16:04 going to use xmpp and a lightweight server (openfire) to inform the backend when it has work to do

16:05 took me a while to get it all figured out but finally got all the clojure code working last night

16:06 pjstadig: cemerick: i think one of the appeals is removing the ORM impedance mismatch

16:07 cemerick: pjstadig: so there's a whole-cluster-JVM persistence mechanism available?

16:08 pjstadig: not sure what you mean...terracotta is just that

16:09 it's not only pitched to replace your database

16:09 one use case (on their site) is to store temporary data in terracotta

16:09 like if you have a wizard interface with stages of completion

16:09 or there example is an exam that someone takes

16:10 only stuff it in your DB when it's complete

16:11 cemerick: they've clarified a lot of things on their site since the last time I was there. Just read http://terracotta.org/web/display/orgsite/Kill+Your+Database

16:11 my last impression was that it was only in-memory clustering

16:12 pjstadig: there's in memory and a permanent store mode that commits to disk and maintains state between restarting Terracotta

16:12 i don't know the history...maybe it wasn't always that way

16:14 cemerick: jeez, if that actually works, that'd be outrageous on a number of fronts.

16:15 WizardofWestmarc: cemerick: exactly!

16:16 and the Terracotta guys are even interested in clojure, so they should be friendly in helping with any problems run into on tying it all together to boot.

16:16 pjstadig: when I signed up at the terracotta site i was contacted by a sales person

16:16 i responded and told him i was just investigating and sent a line to my blog article

16:17 he said he forwarded it to their dev team, and they like to hear about cool integrations like this

16:18 WizardofWestmarc: hm, didn't know there was a terracotta book

16:18 will have to remember that for when I am ready to start playing with it.

16:18 cemerick: we're using jms right now (openMQ), relatively happily, tho. I wonder how applicable that kind of approach is.

16:18 pjstadig: i think terracotta has its own niche

16:19 they're not necessarily trying to replace your database or messaging system

16:20 Chouser: pjstadig: I think it would be a relatively trivial use of gen-class to hold Var roots

16:20 nice writeup, btw, thanks.

16:21 pjstadig: it would be interesting to have an example of that

16:21 cemerick: I'll definitely have to take a look at it.

16:21 pjstadig: maybe you could fork my github

16:21 or maybe i'll play with it

16:21 Chouser: pjstadig: ok, I may look at that tonight.

16:21 apparently I need to get into this whole distributed server thing.

16:22 terracotta, hbase, hadoop, mongodb, and such

16:23 pjstadig: haha!

16:23 hiredman: :(

16:23 pjstadig: information at the speed of twitter!

16:23 WizardofWestmarc: oh my new clojure book beta? *goes*

16:24 hm, looks like it isn't uploaded yet, as it hasn't switched to "regenerate pdf"

16:24 hiredman: is it too much? should I unhook the tweets?

16:25 WizardofWestmarc: hiredman: I'd say yes, anyone who cares can just rss the feed

16:25 that's what I have set up anyway

16:27 cemerick: hiredman: you could hook -clojurebot up to its own twitter account that we could follow :-P

16:28 hiredman: oh, hmmmm, for svn messages?

16:28 WizardofWestmarc: svn messages I like, personally

16:28 cemerick: oh, that'd be handy too, but I meant for the twitter feed as well

16:28 WizardofWestmarc: they don't tend to flood the room but are useful to see

16:29 cemerick: I need to figure out how to get all my rss feeds into something I can follow on twitter.

16:29 WizardofWestmarc: hah

17:04 brennanc: brand new to clojure here. I'm going through the programming clojure book and am trying to figure out how the "examples" folder is included. I created my own folder and put a .clj file in there but I can't load it using "use". What do I need to do so I can use "use"

17:05 Chouser: brennanc: the examples from the git project are loading, but not your own .clj files?

17:10 lenst: /quit

17:16 WizardofWestmarc: wonder how long it takes for a new book upload by the pragprog guys to hit the gerbils

17:19 scottj: Are contrib and slime up-to-date with the fully-lazy changes?

17:19 or clojure-swank rather

17:19 digash`: scottj: yes, i am using it.

17:25 hiredman: what

17:25 I thought I disabled that

17:38 danlarkin: the tweets will never die!

17:39 hiredman: someone should tell them that those stars mean a var that it is intended to be rebound

17:40 * hiredman doesn't have twitter

17:40 Chouser: how ironic

18:04 brennanc: Chouser: I guess I don't really know how the include system works. How do I invoke Clojure and tell it where to look?

18:07 Chouser: brennanc: the simplest way to load a single file is (load-file "foo.clj")

18:07 that'll load "foo.clj" from your current working directory

18:16 brennanc: not able to get that to work. doesn't seem to take a string

18:16 user=> (load-file "my-test.clj")

18:16 java.lang.RuntimeException: java.lang.RuntimeException: java.lang.IllegalArgumentException: Don't know how to create ISeq from: Symbol (my-test.clj:1)

18:20 danlarkin: brennanc: that means my-test has an error

18:20 not the call to load-file

18:21 you probably have an error in your namespace definition, judging from the error

18:28 hiredman: danlarkin: I kind of get the feeling he may not have a namespace declaration

18:29 blbrown: is pircbot still the standard irc bot or has something better come along

18:30 hiredman: pircbot is nice and simple

18:45 twopoint718: I had a facepalm moment just now with using a quoted ns call when it shouldn't have been.

18:47 brennanc: check your ns or in-ns call, it could be the same thing.

19:16 brennanc: ok, thanks, guys. How do I add a directory of .clj files to the class path so I can use it with "use"?

19:19 do I need to jar up the whole folder each time and put that jar in the class file or can I just have it look in a folder?

19:19 Chouser: no need to jar

19:20 for a namespace foo.bar.baz, put the code in a file src/foo/bar/baz.clj, and put src in your classpath

19:21 then you can say (use 'foor.bar.baz)

19:25 brennanc: by put src in my classpath you mean like "export CLASSPATH=$CLASSPATH:/full-path-to/src"?

19:25 Chouser: yes

19:26 note that -cp on the java command line will cause the CLASSPATH env var to be ignored

19:30 brennanc: I modified the repl.sh to add the folder that has my .clj but it still is complaining that it can't find my file in the classpath

19:32 do I need to add a ns at the beginning of my .clj file?

19:32 Chouser: yes

19:34 if you're still having trouble, paste your classpath, .clj file, and the error you're getting

19:34 lisppaste8: url

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

19:37 brennanc: got it. I had to add namespace like "folder.base-file-name". I was just doing "ns folder"

19:37 the repl.sh includes the classpath with a simple "." for the CWD

19:42 Chouser: great

19:47 mrsolo: is there something in closure api so that (eq? 'a 'a) => true?

19:47 Chouser: ,(= 'a 'a)

19:47 clojurebot: true

19:48 Chouser: is that what you mean?

19:48 mrsolo: duh

19:48 i typed = wrong..nm

19:48 yep

19:48 thanks :-)

19:48 Chouser: np

19:50 slashus2: ,(contains? [1 2 3] 3)

19:50 clojurebot: false

19:51 Chouser: ,(contains? '[a b c] 1)

19:51 clojurebot: true

19:55 slashus2: I guess it doesn't work the same as (.contains [1 2 5] 5)

19:56 Works on maps :-|

19:57 Chouser: vectors map from index to value

19:58 'contains?' tells you if the index exists

19:58 slashus2: I get that now.

19:58 Chouser: if you're doing membership checks, a set is faster and more convenient

19:58 ,('#{a b c} 'b)

19:58 clojurebot: b

19:58 Chouser: ,('#{a b c} 'd)

19:58 clojurebot: nil

20:01 slashus2: Okay, neat.

20:02 ,(some #{20} [1 2 20])

20:02 clojurebot: 20

20:02 Chouser: did you read that somewhere?

20:03 slashus2: I was just playing around with some.

20:04 Why?

23:16 danlarkin: so a var is like a thread-local variable, I want to update it with each call to a function

23:16 with atoms I would use swap!

23:16 but I don't want an atom, since that'll be cross-thread

23:17 Chouser: set!

23:18 danlarkin: it complained that it can't change/establish a root binding with set!

23:18 but I have (def *blocks* {}), am I missing something?

23:26 Chouser: ping

23:31 cmvkk: i think you have to use binding first?

23:31 danlarkin: ohh, you are probably right

23:31 Chouser: yep

23:38 binding pushes a thread-local context, and 'set!' can change that.

23:39 to change the root, use alter-var-root, but of course that's shared among threads.

23:41 danlarkin: I will have to find the proper place to establish a binding

Logging service provided by n01se.net