#clojure log - May 28 2009

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

0:16 djpowell: ah k

0:49 DekuDekuplex: Hi, I'm having difficulty in posting a message to the Clojure Google group.

0:51 Every time I post a message, it doesn't appear. Is there some kind of limit to the number of BCC's?

0:52 cads: this is freaky, I'm starting to be able to unconsciously type the correct number of closing parens to a statement, even if it's not clojure

0:53 it's wierd, I won't know how many parens I put down but if I count them it's correct

0:53 DekuDekuplex: Does anybody know how to reach Rich Hickey or the Google group admin? I can't get my message to post.

0:54 cads: what's a BCC?

0:55 DekuDekuplex: Blind Carbon Copy. Similar to CC, for Carbon Copy, except it's invisible to any of the recipients.

0:55 Used to send copies of outgoing mail to other accounts of my own.

0:55 jdz: and besides being invisible it in no way interoperates with the mail sending, i.e. bcc is not your problem.

0:56 DekuDekuplex: Not necessarily. I heard that some servers have a hard limit on the number of recipients (typically 5), including BCC's.

0:56 I suspect that the Clojure group has a limit of <=4 for the total number of recipients who may be included on a posted message.

0:57 jdz: why are there more than 2 recipients of that message is if is being sent to a group?

0:57 DekuDekuplex: If so, then I won't be able to post, because I need to send copies to 4 of my own accounts, plus the recipient, for a total of 5 minimu.

0:57 s/minimu/minimum

0:58 The reason is that I want to have backup copies of my send messages besides one in "Sent Items" in my other accounts.

0:58 s/send/sent

0:58 E.g., copies in Yahoo! Mail, Google Mail, etc.

0:58 One copy for each e-mail account of my own.

0:59 jdz: talk about paranoia

0:59 DekuDekuplex: I have 4 e-mail accounts, so I send out 4 BCC'd copies of each message I send.

0:59 I can lose up to three accounts, but still retain copies of all my sent messages.

0:59 jdz: yeah, that's what paranoic about it

1:00 DekuDekuplex: Useful in a nuclear war, for example.

1:00 jdz: how?

1:00 DekuDekuplex: Less possibility of all the servers losing all their backup data.

1:00 jdz: well, and you are sure you will want to read your sent messages to clojure google group after the bombs go flying?

1:01 eevar2: DekuDekuplex: I imagine you might be subject to moderation until your first message has been approved

1:01 DekuDekuplex: Maybe not bombs, but could be a hurricane, an earthquake, etc. Never know.

1:01 eevar2: farily common google group setting to avoid spammers

1:01 DekuDekuplex: That's another thought, but it's been over 3 hours, and it still hasn't posted.

1:01 Just a technical question about a Clojure program.

1:02 Hm.

1:02 Maybe I can ask the question here.

1:02 jdz: now i want to see this message which in no way can be let to be lost :)

1:03 DekuDekuplex: On slide 20 of the talk "Clojure: A Dynamic Programming Language for

1:03 the JVM" (see

1:03 ftp://lispnyc.org/meeting-assets/2007-11-13_clojure/clojuretalk.pdf),

1:03 by Rich Hickey, the following function and evaluation results are

1:03 defined:

1:03 >(defn argcount

1:03 > ([] 0)

1:03 > ([x] 1)

1:03 > ([x y] 2)

1:03 > ([x y & more]

1:03 > (+ (thisfn x y) (count more))))

1:03 >

1:03 >(argcount 1 2 3 4 5)

1:03 >-> 5

1:03 jdz: use paste please

1:03 DekuDekuplex: However, when I evaluate this function in the REPL, the following

1:03 error is returned:

1:03 >java.lang.Exception: Unable to resolve symbol: thisfn in this context (NO_SOURCE_FILE:6)

1:03 > [Thrown class clojure.lang.Compiler$CompilerException]

1:03 However, no definition is given for "thisfn" in the talk.

1:03 Forgive me if I am missing something very obvious, but does anybody

1:03 know where I can find the definition of "thisfn"?

1:03 That's a copy of the message.

1:03 tWip: thisfn is no more

1:04 DekuDekuplex: How do I use paste?

1:04 jdz: lisppaste8: url

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

1:04 DekuDekuplex: Ok, let me try it then.

1:05 jdz: and replace the thisfn with argcount

1:06 DekuDekuplex: and i hope you have noticed that it's year 2009 now and that clojure has a version 1.0 release, and much has changed since the date of that .pdf?

1:07 DekuDekuplex: Hm ... I was looking for a good tutorial for functional programmers, but all I could find was one with a lot of examples and not much theory, and a for-fee book.

1:07 jdz: maybe time flies by faster when you think about bomb shelters half of the time...

1:07 DekuDekuplex: Then I came across those slides.

1:07 lol ;)

1:07 jdz: just get clojure-contrib and you'll find loads of code to read

1:08 DekuDekuplex: Er ... I was looking for something similar to the book Programming in Haskell, but for Clojure students.

1:08 jdz: you'll have to wait for the clojure book to come out, then

1:08 or get the pdf version

1:09 DekuDekuplex: Thats a for-fee book, IIRC?

1:09 lisppaste8: DekuDekuplex pasted "a question about the definition of the variable arity function in "Clojure: A Dynamic Programming Language for the JVM," by Rich Hickey" at http://paste.lisp.org/display/80963

1:10 jdz: DekuDekuplex: what's wrong for asking fee for one's work

1:10 ?

1:10 DekuDekuplex: Ah, finally I got it to paste. Just pasted the earlier post.

1:10 I'm broke this month.

1:10 Until my next paycheck on June 20.

1:10 Also, I live in Tokyo, where costs are very expensive.

1:11 No credit card right now, either.

1:11 jdz: and, btw, i already told you the solution to your problem...

1:11 DekuDekuplex: Yes, use argcount instead of thisfn, right?

1:13 jdz: reading actual real-world code is the best way to learn a programming language.

1:14 DekuDekuplex: That's useful if you want to write an actual application. If all that is wanted is to compare semantics from a PLT perspective, then a theory book is more useful, though.

1:15 jdz: PLT?

1:15 DekuDekuplex: Programming Language Theory

1:15 I used to study formal semantics in college. Interested in the lambda calculus.

1:15 jdz: you can't get PLT semantics out of tutorial code, you know that, right?

1:16 at least not more than from code that actually does something interesting

1:16 DekuDekuplex: Right, but I started out comparing how to write Towers of Hanoi in Haskell, Scheme, and Clojure, and was looking for a way to move on to other topics along that line.

1:17 Clojure is very interesting, because it looks like a combination of Lisp and Java.

1:17 jdz: not really, not to me at least.

1:17 there is not much Java in clojure language

1:18 (the little that is are the Java interop things)

1:18 DekuDekuplex: Hm. I was especially surprised that (cond ...) statements didn't require parentheses for each condition.

1:18 They do in Scheme and Common Lisp.

1:18 It was much easier to write Towers of Hanoi in Clojure than in Common Lisp, so I wanted to pursue Clojure further.

1:19 The problem with Scheme is that each implementation has some kind of problem.

1:19 jdz: what was easier?

1:19 DekuDekuplex: Easier to figure out how to write.

1:19 jdz: i mean compared to Common Lisp?

1:19 DekuDekuplex: Yes, definitely.

1:19 jdz: you mean it's easier to write 10 lines of clojure code than Common Lisp code?

1:20 DekuDekuplex: For that particular algorithm, yes.

1:20 Easy to figure out from the examples.

1:21 I got stuck in Common Lisp when the "(progn ...)" statement behaved differently from the "(begin ...)" statement in Scheme with respect to recursive calls of "(format ...)."

1:22 But then "(do ...)" in Clojure pretty much behaved as I expected, so writing Hanoi was a breeze.

1:22 jdz: i can't imagine how that would look like

1:22 DekuDekuplex: Which part?

1:22 jdz: the recursive calls to format in progn

1:23 DekuDekuplex: Well, let me paste some code that I have written for Hanoi in Scheme and what I tried to write in Common Lisp, and what I wrote in Clojure.

1:23 One second....

1:25 lisppaste8: DekuDekuplex annotated #80963 "PLT-Scheme-specific Version 1.1.1 of Towers of Hanoi" at http://paste.lisp.org/display/80963#1

1:25 DekuDekuplex: That was my Scheme version of Towers of Hanoi.

1:26 jdz: there is no (begin ...)

1:27 DekuDekuplex: In particular, the part after the "(else ...)" recursively calls "(hanoi-helper ...)", and the "(printf ...)" statements are all correctly displayed.

1:27 jdz: ok, and the CL version?

1:27 DekuDekuplex: Right, but I can also use "begin" for the middle part.

1:27 jdz: and clojure please

1:27 DekuDekuplex: Ok.

1:27 One moment....

1:29 lisppaste8: DekuDekuplex annotated #80963 "PLT-Scheme-specific Version 1.1.1 of Towers of Hanoi" at http://paste.lisp.org/display/80963#2

1:30 DekuDekuplex: That's my (non-working) CL version.

1:30 (hanoi 1) works, but not (hanoi 2).

1:31 Only the final "(format ...)" statement is actually executed.

1:31 That's where I got stuck.

1:31 Then I got frustrated, and tried Clojure.

1:31 Now for the Clojure version....

1:32 lisppaste8: DekuDekuplex annotated #80963 "Clojure version of Towers of Hanoi" at http://paste.lisp.org/display/80963#3

1:33 Chousuke: DekuDekuplex: defn inside defn is not good style.

1:33 DekuDekuplex: Recursive calls to "(hanoi-helper ...)" correctly invoke multiple calls to "(format ...)".

1:33 Chousuke: defn always makes global functions; if you want locals, use (let foo [(fn ...)])

1:33 DekuDekuplex: I wanted to use "(lambda ...)", but there is no lambda in Clojure.

1:33 Chousuke: it's called fn

1:33 DekuDekuplex: Ah, I get it.

1:34 Chousuke: there's also a quick syntax for them: #(foo % %1 %2 %&)

1:34 DekuDekuplex: So replace "(defn hanoi-helper [n source dest using] ...)" with "(let hanoi-helper [n source dest using] ...)"?

1:35 Chousuke: where % == %1, and %n means (nth parameter), and %& is the "rest parameter"

1:35 djpowell`: it would be (let [hanoi-helper (fn [n source dest using] ... )] ...)

1:35 Chousuke: oops. intended quotes instead of parens ther e:P

1:35 djpowell`: i think

1:35 Chousuke: yeah

1:36 and if it needs to call itself recursively, you need to do (let [hh (fn hh [params] ... (hh))] or use recur

1:36 DekuDekuplex: So "(let "hanoi-helper (fn [n source dest using] ...)" ...)"?

1:37 Single-quotes?

1:37 Chousuke: a vector

1:37 DekuDekuplex: Brackets?

1:37 Chousuke: ,(let [x 1 y 2] (+ x y))

1:37 clojurebot: 3

1:38 djpowell: let takes square brackets around its args which are pairs of varname/values

1:38 DekuDekuplex: Ok.

1:38 djpowell: it is a convention in clojure for other let-ish things to do that too

1:39 jdz: DekuDekuplex: i suggest you go read documentation of CL's format function before spreading more uninformed assertions.

1:40 DekuDekuplex: So "(let [hanoi-helper (fn [n source dest using]) ...] ...)"?

1:40 Chousuke: yeah

1:41 DekuDekuplex: Ok, let me rewrite that earlier Clojure function using that syntax. One minute, and I will post it....

1:41 Chousuke: though as I said, if hanoi-helper calls itself recursively, you need to give it a name after the "fn" part too; it can't see the name in the let

1:42 ,(let [v [1 2]] (let [[x y] v] (+ x y))); let can also do this. note how the structure of the items matches.

1:42 clojurebot: 3

1:42 jdz: and a general observation: comparing programming languages one does not know from PLT perspective (well, any perspective) is useless

1:43 lisppaste8: DekuDekuplex annotated #80963 "Revised Clojure Towers of Hanoi function" at http://paste.lisp.org/display/80963#4

1:43 DekuDekuplex: I haven't tested this one yet. Let me test it.

1:44 java.lang.Exception: Unable to resolve symbol: hanoi-helper in this context (NO_SOURCE_FILE:6)

1:44 [Thrown class clojure.lang.Compiler$CompilerException]

1:44 Nope.

1:44 Ah, need to give it a name.

1:44 Ok....

1:45 jdz: DekuDekuplex: make sure you back up this IRC session in at least 4 places so you can read it once a day for following weeks

1:45 maybe you'll learn something

1:45 DekuDekuplex: I'm already backing it up.

1:45 Just saved it.

1:46 Can I give it the same name after "let" and after "fn"?

1:48 lisppaste8: rhickey annotated #80963 "hanoi using letfn" at http://paste.lisp.org/display/80963#5

1:48 DekuDekuplex annotated #80963 "This Clojure code didn't work...." at http://paste.lisp.org/display/80963#6

1:49 DekuDekuplex: letfn! Interesting!

1:49 opqdonut: is there a nice way to concat two vectors and keep the result a vector

1:49 short of something like (comp vec concat)

1:50 rhickey: opqdonut: (into v1 v2)

1:50 ,(into [1 2 3] [4 5 6])

1:50 clojurebot: [1 2 3 4 5 6]

1:50 opqdonut: ah, great!

1:51 DekuDekuplex: Yes! That worked!

1:52 Use letfn instead of fn for a recursive call. So letfn is like a lambda closure?

1:52 jdz: DekuDekuplex: now go change the first parameter of format call from nil to t and reflect on any insights you get. please.

1:53 DekuDekuplex: In my CL code?

1:53 rhickey: letfn is like Scheme letrec or CL labels, limited to fns only

1:53 jdz: DekuDekuplex: yes, your CL code.

1:53 DekuDekuplex: Ah, letrec. Ok.

1:53 Recursive version of let.

1:55 Ok, (was answering a question from someone just now), let me change it now.

1:55 rhickey: you could also have done (let [hh (fn hh [args] ... (hh ...))] (hh ...))

1:59 DekuDekuplex: I just changed the CL function and tried it, and this time it returns nil.

1:59 s/nil/NIL

2:00 Hm ... let me look up some documentation for the format function....

2:02 t is supposed to be for standard output, according to http://www.gigamonkeys.com/book/a-few-format-recipes.html.

2:03 But all it does is returns NIL. Hm....

2:05 Somehow I get the feeling that this problem isn't about format, but about how to get CL to allow multiple side-effects in a sequence of recursive calls.

2:05 jdz: nope

2:05 DekuDekuplex: It's about format?

2:05 jdz: if you'd translate the code directly, you'd also put a newline after the output

2:08 DekuDekuplex: As in "\n"?

2:08 hiredman: DekuDekuplex: clojure.core/format is more like java's Format then CL's

2:08 jdz: that won't work in CL

2:08 DekuDekuplex: How about "(newline)"?

2:08 jdz: try ~%

2:09 or just (terpri) after format

2:09 DekuDekuplex: Still same result.

2:09 Just NIL.

2:10 jdz: well, at this point i somehow don't trust any of your observations...

2:10 DekuDekuplex: That line reads "(cond ((equalp n 1) (format t "Move disc from ~A to ~A.~%" source dest))".

2:11 Let me paste the entire CL program.

2:11 jdz: DekuDekuplex: depending on your implementation, you might want to add a call to finish-output after format

2:12 lisppaste8: DekuDekuplex annotated #80963 "broken CL Towers of Hanoi program and result" at http://paste.lisp.org/display/80963#7

2:13 jdz: that version works like charm for me on SBCL

2:13 DekuDekuplex: I'm using GNU CLISP.

2:13 jdz: well, try calling (finish-output) after the (hanoi 3) call

2:14 i never liked clisp myself

2:15 lisppaste8: DekuDekuplex annotated #80963 "finish-output didn't work...." at http://paste.lisp.org/display/80963#8

2:15 DekuDekuplex: Nope, that didn't seem to work, either. Just pasted the result.

2:16 I can guess why.... ;)

2:16 jdz: well, i've said enough on this offtopic

2:16 DekuDekuplex: Now you see why I like Clojure more than CLISP in this case.

2:17 jdz: CLISP != Common Lisp

2:17 rsynnott: DekuDekuplex: #lisp, perhaps?

2:17 DekuDekuplex: Indeed.

2:17 rsynnott: DekuDekuplex: you muight want force-output

2:17 jdz: and not liking things because you don't understand them is not very productive

2:17 cads: hey, I've looked at some common lisp code recently, and reading it I got the feeling of old and dusty punchcards, and when I translated it to clojure it felt a lot crisper for some reason... is this just my subjective experience, as in would a clisper feel the same way about clojure not being traditional?

2:17 DekuDekuplex: Well, I used to use CLISP in college, too, and had similar problems back then, too.

2:18 Probably.

2:18 rsynnott: (also, clisp the implementation is not representative of modern cl impls)

2:18 DekuDekuplex: It's hard to get SBCL to work with SLIME on Windows XP.

2:19 cads: I was referring to popular, common lisp, not necessary gnu clisp

2:19 DekuDekuplex: (force-output) after (hanoi 3) had the same result....

2:19 Which implementation for Windows XP and SLIME?

2:20 jdz: the problem is not the output functions of specific common lisp implementations, it's about jumping to wrong conclusions. (this all started with my curiosity about recursive calls to format in progn...)

2:21 DekuDekuplex: As in what the cause of the problem was: format vs. side-effects?

2:21 jdz: no. there has never been any issue with progn/format interaction

2:22 DekuDekuplex: Hm. Because when I type very similar code in PLT Scheme, it works fine.

2:22 jdz: as opposed to what you stated at the beginning

2:22 DekuDekuplex: Same for Clojure.

2:22 But not CL, for some reason. Strange....

2:22 jdz: that's the problem. you write similar code in different programming languages and expect your code to be perfectly correct in those languages.

2:23 you can't compare apples to oranges thinking that they are all bananas

2:23 DekuDekuplex: Interesting analogy.

2:23 rsynnott: DekuDekuplex: ideally, do not use common lisp on win32

2:23 however, ccl works okay these days

2:24 DekuDekuplex: No kidding. The problem is, I only have time to study at the office during brief respites, and no time at home, so I'm stuck with win32.

2:25 Can't really install Linux on an office computer.

2:25 I do have a Mac at home, but no time to use it.

2:25 rsynnott: ccl, then

2:25 jdz: DekuDekuplex: then go download LispWorks and you'll have much better experience.

2:25 DekuDekuplex: LispWorks ... hadn't thought about that.

2:25 I thought it'd be the same, because it's also Common Lisp.

2:26 I heard there was a free version that doesn't allow saving stand-alone executables.

2:26 jdz: well, if there were no differences between implementations, why do you think there are more than one?

2:27 DekuDekuplex: I thought the differences would be minor, as in SWI-Prolog vs. GNU Prolog.

2:27 jdz: based on what?

2:27 DekuDekuplex: Well, I've tried several different implementations of Scheme, for instance, but they all work similarly.

2:28 PLT, MIT/GNU, Gauche, etc.

2:28 So I thought it was similar for Common Lisp.

2:28 jdz: you thought wrong. just accept it and move on.

2:28 i'll just shut up.

2:29 DekuDekuplex: Hey, no need to get so drastic! I'll give LispWorks a try.

2:29 eevar2: DekuDekuplex: /join #lisp

2:30 DekuDekuplex: The other problem with Common Lisp is that it's hard to ask anything on comp.lang.lisp without somebody coming out and making fun of the question.

2:30 There are certain ... denizens ... who inhabit comp.lang.lisp, who have a funny sense of etiquette.

2:31 I'm used to USENET, but it's hard to find an alternative newsgroup.

2:31 Ok.

2:31 I'll go try #lisp, too. Maybe my mileage will vary....

2:32 I hope they aren't the same people who inhabit comp.lang.lisp.

2:32 A few weeks ago, I tried creating a newLISP newsgroup by getting support on comp.lang.lisp, and a lot of the people there just made fun of newLISP.

2:33 Are they the same people?

2:33 rsynnott: DekuDekuplex: in fairness, newlisp deserves to be made fun of

2:34 but as long as you're polite #lisp is fine these days

2:34 alinp: hi

2:34 DekuDekuplex: Well, now that I know it better, I can see that it has a lot of problems, but they compared it to "jack****."

2:34 (I can't use that term here.)

2:34 alinp: does clojure have a special tuple data structure ?

2:34 or a sequence can be considered a tuple ?

2:35 hiredman: alinp: most people use vectors as a sort of tuple

2:35 DekuDekuplex: Ok, gotta go now. Thanks for the help. Session saved. Will go look at #lisp.

2:35 Bye.

2:35 jdz: well, depends on what you mean by "tuple"

2:35 hiredman: handy because you don't have to worry about quoting

2:35 alinp: something like erlang's tuple

2:35 jdz: if it's something with first and rest, then yes, sequences are the tuples

2:35 alinp: in fact, I have other q

2:36 in erlang tuples are used within pattern matching; can we speak about pattern matching in clojure ?

2:37 hiredman: doesn't have it, so speaking about it would be silly

2:37 alinp: :) ok

2:37 hiredman: but you can sort of do stuff like http://www.thelastcitadel.com/blag/clojure_multi-method

2:37 alinp: yes, I heard about it

2:37 thanks

2:38 btw, that multi-method is pretty neat

2:47 java.lang.Exception: The syntax for defmulti has changed. Example: (defmulti name dispatch-fn :default dispatch-value) (test_defmulti.clj:1)

2:47 running the example that hiredman gave

2:47 (http://www.thelastcitadel.com/blag/clojure_multi-method)

2:51 hiredman: yeah

2:51 that code is like six months old

2:52 (defmult fib identity :default :default)

2:52 defmulti

2:52 alinp: yes, this is how I made it

2:52 so it seems that is ok :D

2:53 wasn't sure that :default :default is not a nonsense

2:53 :)

3:05 rhickey: the default is :default, so you can just leave that out

3:07 hiredman: oh

3:08 well thats nice

3:12 hoeck: ,(count (apply concat (repeat 10000 nil)))

3:12 clojurebot: 0

3:12 hoeck: ,(count (apply concat (repeat 30000 nil)))

3:12 clojurebot: java.lang.StackOverflowError

3:12 hoeck: is this a bug in concat?

3:12 rsynnott: poor machine

3:12 you are in effect trying to pass a function 30,000 parameters

3:13 some sort of reduce type thing might be more appropriate in the circumstances

3:13 hoeck: or must i expect the stack-consuming behavior of concat?

3:13 rsynnott: its the same when using reduce

3:14 ,(count (reduce concat (repeat 30000 nil)))

3:14 clojurebot: java.lang.StackOverflowError

3:20 rhickey: hoeck: in the enhancements I made for chunked seqs I made lazy-seq smarter about nested seqs - what's happeining with concat a bunch of nils is a long chain of lazy-seqs is created, evaluating the first of which recursively calls the rest

3:20 ,(count (reduce concat (repeat 30000 [1])))

3:20 clojurebot: java.lang.StackOverflowError

3:21 rhickey: here:

3:21 user=> (count (apply concat (repeat 300000 [1])))

3:21 300000

3:21 (it's good to be here :)

3:22 hoeck: rhickey: so the problem is solved on you machine with the chunked-seqs implementation?

3:23 rhickey: yes, not really relying on the chunked seqs, just an enhancement to lazy-seq

3:24 I'll try to move the chunked work onto trunk - kind of frustrated with having it be in a git repo I can work on on only one machine

3:24 hiredman: erm

3:25 Chousuke: what's preventing you from cloning the repo somewhere else?

3:25 hiredman: ?

3:25 rhickey: with git svn there are caveats about using it as you would normally in git, since changes can't get synched back correctly

3:26 Chousuke: the new clone is "pure git", but you can push its changes back to the svn-git repo and dcommit from there

3:26 rhickey: see caveats here: http://www.kernel.org/pub/software/scm/git/docs/git-svn.html

3:27 I'm not going to start sending myself patches when I switch machines

3:28 and if I can only synch properly by dcommitting I might as well work on trunk

3:29 hoeck: rhickey: okay, good to know its fixed, it took me about 4 hours to find the cause of the stackoverflow in my code

3:29 Chousuke: rhickey: hmm :/

3:30 SVN really complicates working with git ;(

3:31 rhickey: Chousuke: apparently. I wanted to try it, but overall I don't think it is really giving me the full git experience

3:32 just left me uncomfortable with a chunk of my work stuck on one machine and not on a server

3:49 Chouser: inotify calls its handlers "watches", as in "add_watch", "remove_watch"

4:35 * rhickey git svn dcommit

4:36 * Chouser git svn rebase

4:36 rhickey: already did that

4:36 it's up

4:37 Chouser: yes, I meant since you did a dcommit, I did a rebase.

4:37 rhickey: ah

5:06 cemerick: rhickey: do you have a high-level writeup of chunked seqs and/or the instance stuff somewhere (like what you did with clojure.org/lazier for some period)?

5:07 rhickey: cemerick: not yet, I will, but they are not exposed in the api yet in svn

5:10 cemerick: From what I've gleaned from irc logs, instance looks particularly interesting to me. Perhaps something that can replace my genbean contraption.

5:10 Chouser: instance will be very cool

5:11 rhickey: yes, instance is a key to less Java in Clojure impl

5:11 * rhickey says, as he adds more classes to core...

5:12 cemerick: I should post a revised genbean. Looking back at my original postings, that first code was positively *fugly*.

5:21 Chousuke: hmm.

5:22 jdz: is there any var that would tell me if code is being compiled?

5:23 i want to execute some code when a namespace is being loaded, but not when it is being compiled

5:24 (it is related to loading C library)

5:25 cemerick: jdz: I think *compile-path* is always set when one is compiling

5:25 Chouser: ,*compile-files*

5:25 clojurebot: false

5:25 cemerick: (or, at least, java.lang.Compile always sets it -- I never compile from the REPL)

5:25 Chousuke: hm

5:25 * Chousuke notices definline is still markedas experimental

5:26 Chouser: jdz: You want to check *compile-files*. *compile-path* defaults to "classes" even when you're not compiling.

5:26 Chousuke: it's also used only four times in the core code :P

5:27 jdz: Chouser: yes, *compile-files* is exactly what i need

5:27 Chousuke: all others use :inline metadata directly it seems.

5:28 cemerick: jdz: sorry for the bad pointer :-/

5:30 jdz: works like charm

5:41 btw, i was looking in the http://clojure.org/compilation for the name if this variable, and it is not mentioned there...

5:43 lisppaste8: rhickey pasted "play along w/chunked seqs" at http://paste.lisp.org/display/80967

5:46 jdz: oh, too bad the times are not included in the comments

5:46 rhickey: vector seqs are now chunked, showing transparent interop with chunk ignorant code

5:47 still need names for the buffer, chunk and chunked first/rest/next stuff

5:48 jdz: summary - chunked seqs rule

5:48 jdz: well, that i can gather from your excitement :)

5:49 rhickey: the map*/reduce*/filter* will go away, chunked seqs will be tested for in normal map/reduce/filter

5:49 Chouser: you'll want a chunked-cons too?

5:50 rhickey: Chouser: yes

5:51 Chouser: these new names will show up only in code that's already expending extra effort to perform well

5:51 so if they're a little long it's not too big a deal, right?

5:51 although chunked-cons, chunked-rest, etc. seem awfully verbose

5:52 jdz: rhickey: why do you need separate functions for first/rest/next?

5:52 Chouser: jdz: chunked-rest means the next *chunk*, not the next item.

5:52 bah, not quite

5:52 rhickey: jdz: because they do different things, chunked seqs still support first and it returns 1 item, rest returns other than the first item etc

5:52 jdz: yes, but if the sequence is chunked, you'd get next chunked-cons

5:53 Chouser: jdz: chunked-first means the first *chunk*, not the the first item.

5:53 jdz: implementtion details

5:53 rhickey: jdz: just because the seq you have is chunked doesn't mean it's rest/next is also

5:53 jdz: implementation

5:53 rhickey: jdz: not at all

5:53 first and chunked-first return 2 different things

5:54 a chunked seq is not a seq of chunks

5:54 Chouser: chunked-first fails on a non-chunked seq?

5:54 jdz: yes, that's clear

5:54 but for me as a user, it does not matter if the sequence is chunked or not

5:55 for me first would return first item from the seqence, never mind the minor detail that next chunk was pulled off of it

5:55 Chouser: jdz: right, you can ignore the chunkiness and write correct code that may or may not be faster than it used to be.

5:55 rhickey: but there are still 2 functions that do different things and need different names

5:56 jdz: well, yes. for those who want to take advantage of chunkiness. got it.

5:56 {newbie}: Hi is it idiomatic to use struct-maps as union types?

5:57 rhickey: Chouser: I'm wary of making them too long

5:57 Chouser: {newbie}: leaving various subsets of the values as nil?

5:57 rhickey: Chouser: yes, chunked-first/rest/next require chunked seqs, the presumption being all correct code will be testing for chunked or downstream logic for code that tests

5:58 Chouser: rhickey: ok

5:58 mattrepl: chunked sequence would allow for, say, building sequences from paginated data? e.g., grabbing a page of results where each each result is an item in the sequence and each page is a chunk?

5:58 {newbie}: Chouser: no

5:58 Actually I have a much bigger problem

5:58 how do I organize a clojure problem

5:58 program*

5:59 I have some experience with ocaml

5:59 but without static stypic to guid the module coding

5:59 i'm a bit lost

5:59 rhickey: mattrepl: chunked seqs provide an advantage when the chunks can support indexed access

6:00 i.e. lists will never provide chunked seqs

6:00 Chouser: {newbie}: that's a very broad question. Have you looked on the google group? Various forms of that question come up from time to time.

6:01 rhickey: anyway, before I move chunks into map/reduce/filter I want to see that everything is ok with vector seqs being chunked. Clojure itself, contrib and its tests seem ok

6:02 JoelMcCracken: what is the best way to get the contents of a url into a string?

6:02 {newbie}: Chouser: thanks I think I found a thread on the subject

6:04 hiredman: JoelMcCracken: http://gist.github.com/119375

6:05 JoelMcCracken: nice, that come up a lot or have you just had it around?

6:06 hiredman: I had it around, and I have nice vim plugin that dumps selected text to gist

6:06 JoelMcCracken: niiice

6:06 Chouser: I'm sure what hiredman has is much more correct, but this also works: (with-open [s (.openStream (java.net.URL. "http://google.com/&quot;))] (apply str (map char (take-while pos? (repeatedly #(.read s))))))

6:07 JoelMcCracken: ah, cool. thanks for that, too

6:09 Chouser: hm, I bet mine doesn't do unicode correctly.

6:10 hiredman: any reason you're not using (apply str ...)?

6:10 lisppaste8: jdz pasted "stream slurping lying in my utility package" at http://paste.lisp.org/display/80970

6:10 tashafa: when is the next clojure boston meeting?

6:10 clojurebot: clojure is like life: you make trade-offs

6:10 jdz: here, no reading by single characters and applying str later

6:13 and besides, you cannot properly read unicode by trying to convert single bytes to characters

6:14 cemerick: ooh, I didn't know there *was* a boston clojure meeting :-)

6:14 hiredman: Chouser: I think I was following some java code

6:14 Chouser: right, but using a LineReader like hiredman should handle unicode just fine.

6:15 hiredman: ah, sure.

6:15 jdz: yes, *Reader should be fine

6:15 Chousuke: reading a single byte at a time sounds slow though ;(

6:15 Chouser: Does BufferedReader read a single byte at a time?

6:15 jdz: that's why everybody should use the my awesome buffered reader :)

6:16 Chousuke: Chouser: .read does, doesn't it? and if you map over that, there will be the function call overhead for every byte.

6:16 jdz: Chouser: well, you get one character at a time, but OS reads are in chunks

6:16 that's the whole purpose of buffered things

6:16 Chousuke: Chouser: though I bet chunked seqs will help with this too :D

6:16 Chouser: Chousuke: yes. please ignore my bad example. It is brief but incorrect and inefficient.

6:22 replaca: I've noticed that contrib has been *very* stable the last few weeks

6:44 cemerick: have any enterprising git-svn users tried maintaining local patch sets against clojure's svn?

6:44 Chouser: yes

6:45 I was maintaining clojurescript-required changes to clojure proper as a git branch for quite a while.

6:46 cemerick: Chouser: I assume doing so works well, then? So far, we've just been roping in clojure jars as things go along, but now I'd like to start cherry-picking patches, etc.

6:46 ah, nice

6:46 I've just never maintained a patch queue like that. Good to know it works nicely.

6:47 danlarkin: probably best to use branches, have master be unadulterated svn mirror and then pull that into your changes branch

6:47 I guess(TM(TM)

6:47 Chousuke: I do that with every git repo I have.

6:47 cemerick: heh

6:48 Chousuke: master always represents my view of the upstream; I never commit anything on it.

6:48 that is, unless I actually need to push stuff :P

6:48 cemerick: yeah, that's roughly what I had in mind, although I hadn't thought it through entirely.

6:49 is there a form like -> that will just return nil if any of the exprs return values is nil?

6:49 Chousuke: -?> in contrib I think

6:51 cemerick: ah, next to defnilsafe.

6:51 -?> is hard to look at though ;-)

6:54 Chousuke: it makes me think of XML :P

6:54 cemerick: yeah. like a PI

7:11 Chouser: bother. jna.Structure appears to require the use of reflection to find a Class's list of fields and their types.

7:37 danlarkin: Anything clojure-related happening in Boston the weekend of June 26-28? I'll be in town

7:42 {newbie}: :| I'm afraid I am going to use closure as a glorified imutable gc'ed C

7:43 and functional

7:43 gnuvince: So you're going to use it TOTALLY unlike C is used?

7:43 JoelMcCracken: clojure's macros don't seem very documented; are they similar to common lisp's

7:43 ?

7:43 gnuvince: JoelMcCracken: yes

7:43 JoelMcCracken: essentially the same?

7:44 gnuvince: JoelMcCracken: pretty much, yes.

7:44 hiredman: clojure's macros are functions

7:44 Chouser: hiredman: implementation detail

7:44 hiredman: Chouser: an excellent one

7:44 Chouser: JoelMcCracken: Clojure uses ~ and ~@ instead of , for inserting things into syntax-quoted forms.

7:45 gnuvince: iirc, the only difference is the capture of variables.

7:45 Chouser: JoelMcCracken: and plain symbols in syntax-quote become fully-qualified to improve hygine.

7:45 JoelMcCracken: is thie documented anywhere?

7:46 viator_sg: Anybody have tried to build clojure-android recently?

7:46 dnolen: also popular Lisp implementations warn on undefined symbols in macros, Clojure will throw an exception.

7:48 {newbie}: does anyone have an ant script that just compiles the java classes but runs the clojure scripts in the repl?

7:48 Chouser: JoelMcCracken: official (sparse) docs under "Syntax quote" here: http://clojure.org/reader

7:48 dnolen: http://en.wikibooks.org/wiki/Learning_Clojure#Macros

7:49 JoelMcCracken: oh, fun

7:49 thanks

8:28 {newbie}: is there away to make a nonpublic def?

8:28 like the defn- for functions?

8:28 kotarak: (def #^{:private true} bla 5)

8:28 clojure.contrib.def has defvar and defvar-

8:28 {newbie}: well

8:29 it's a mutable collection but it's not a var

8:29 thanks!

8:29 kotarak: def defines a Var...

8:29 {newbie}: nvm im throwing scala in the mix

8:29 :\

8:35 hiredman: http://www.yosefk.com/blog/the-high-level-cpu-challenge.html <-- comments are interesting

8:46 Lau_of_DK: Very :)

8:53 hiredman: clojurebot: ping?

8:53 clojurebot: PONG!

8:53 hiredman: clojurebot: problem?

8:53 clojurebot: not a problem, the average bid for it on getacoder is $821.00

8:54 cp2: lol

8:54 i love you clojurebot

8:54 hiredman: clojurebot: problem is <reply>"There is no problem in computer programming which cannot be solved by an added level of indirection." -- Dr Maurice Wilkes

8:54 clojurebot: Ack. Ack.

8:55 hiredman: (except performance)

8:56 kotarak: clojurebot: problem is also <reply>People have a problem and think "Hey! I'll use a regular expression!". Now they have two problems....

8:56 clojurebot: You don't have to tell me twice.

8:58 cp2: ahh i love a good helping of propoganda: http://itsbetterwithwindows.com/

9:05 alinp: hi

9:05 I have the following situation:

9:05 (def x (agent nil))

9:05 (send x (fn [] nil))

9:06 java.lang.IllegalArgumentException: Wrong number of args passed to: user$eval--502$fn

9:06 user=> (send x (fn [_] nil))

9:06 java.lang.RuntimeException: Agent has errors (NO_SOURCE_FILE:0)

9:06 what is happening here ?

9:06 Chouser: ,(doc agent)

9:06 clojurebot: "([state] [state & options]); Creates and returns an agent with an initial value of state and zero or more options (in any order): :meta metadata-map :validator validate-fn If metadata-map is supplied, it will be come the metadata on the agent. validate-fn must be nil or a side-effect-free fn of one argument, which will be passed the intended new state on any state change. If the new state is unacceptable, the validate-fn

9:06 Chouser: bah

9:06 ,(doc send)

9:06 clojurebot: "([a f & args]); Dispatch an action to an agent. Returns the agent immediately. Subsequently, in a thread from a thread pool, the state of the agent will be set to the value of: (apply action-fn state-of-agent args)"

9:07 alinp: oh

9:07 Chouser: your action-fn needs to take at least one arg, the state-of-agent

9:07 alinp: yes yes

9:07 kotarak: alinp: the second time you sent, there is already an error from the first try.

9:07 alinp: I know that, but if I'm missing it for the first time, my agent is lost ?

9:07 Chouser: oh, I see. no, the agent is in an error state.

9:07 (count (agent-errors x))

9:08 alinp: so ... what can I do about it ?

9:08 Chouser: (.printStackTrace (first (agent-errors x)))

9:08 alinp: what is the usual (elegant) solution for this situation ?

9:08 the only solution is to create other one ?

9:08 Chouser: ,(doc clear-agent-errors)

9:08 clojurebot: "([a]); Clears any exceptions thrown during asynchronous actions of the agent, allowing subsequent actions to occur."

9:08 alinp: oh

9:08 :)

9:08 this is what I was looking for

9:08 thanks

9:08 Chouser: np

9:09 alinp: but, why is this behavior ?

9:09 I mean, why it stays in an error state ?

9:09 why remembers the error state ?

9:09 clojurebot: why not?

9:09 Chouser: heh

9:09 ataggart: lol

9:10 alinp: why test ?

9:10 Chouser: well, surely you'd want the exception to be available somewhere so you can see what happened... what better place than in the agent itself?

9:10 alinp: wasn't a bot the clojurebot ?

9:10 :)

9:10 hmmm, Chouser , true

9:10 it kind of make it sense, when you put it like this ...

9:11 Chouser: Other options have been discussed. There'll probably be a way in the future to register a fn that would get called when an exception happens.

9:11 alinp: yep, like a callback ..

9:11 I think it will be useful, for me at least

9:12 Chouser: sure. in that case, perhaps the callback could return a value indicating if the agent's error state should be cleared or not.

9:12 alinp: yep, nice

9:12 Chouser: or perhaps that's never necessary and the error state would be cleared automatically and continue, what the callback is handled in another thread

9:12 alinp: alright then, thanks once again

9:13 Chouser: do you have an opinion or use case that would recommend one particular improvement over another?

9:13 alinp: maybe a parameter to agent construction

9:13 Chouser: none of these options would be hard to implement, but rhickey doesn't seem to have a concrete idea of how it should be, and it's not something that would be easy to change once people start using it, so you want to get it right.

9:13 alinp: that will indicate if to "forget" the error ... or to handle it in a specific way

9:14 Chouser: in your use cases is it ever useful for the agent to remain in an error state?

9:14 alinp: yes, I totally agree, I kind of hate when api is changeing

9:14 ataggart: in such an exception callback function, one couls just call (clear-agent-errors *agent*) right? So maybe the only thing it should return is a new agent state

9:15 alinp: well, I don't think it's usefull at all

9:15 I mean, you should check every time if the agent is "dead" or not

9:15 right ?

9:15 or to call every time the clear function

9:15 Chouser: ataggart: yeah, that's what I was suggesting yesterday, but rhickey seemed to prefer to not rely on the agent-errors/clear-agent-errors api in this new callback mechanism.

9:16 ataggart: I'm not sure why.

9:16 alinp: definitely the error state should be loged somehow

9:16 ataggart: the main reason I liked it was that it would allow yielding a new agent state

9:16 alinp: but, I don't think it should stop the agent working and force me to do extra checking

9:17 Chouser: it sometimes seems ok (esp. if there's no error callback) to have the agent in an error state so next time you look at it you see the error.

9:17 alinp: myes...

9:17 you look at it by sending some function

9:18 Chouser: ,(let [x (agent :ok)] (send-off x #(:oops)) (await x) (println "x is" @x))

9:18 clojurebot: java.lang.Exception: Agent has errors

9:18 ataggart: absent the callback, erroring out the agent seems like the best alternative. It means you have to babysit the agent, but at least errors are dealt with.

9:18 Chouser: I mean look at by sending or deref'ing

9:19 ataggart: hmm... of course wouldn't all this be obviated by simply including try/catch in the action?

9:19 alinp: of course ...

9:19 damn ... try / catch it takes me back to java ... and I hate it :)

9:19 Chouser: ataggart: almost always, yes. It's often what I do now.

9:20 ataggart: put that way, your action can "decide" whether or not to error out the agent by propagating the exception, or it could just do what it needs to do to yield the right/new agent state.

9:20 so your "callback" is just built into the action

9:20 alinp: yep, that's true

9:21 Chouser: one other case though is if the error comes from outside the action itself. The only time I've seen that is when the agents are being shut down while still trying to send.

9:21 ataggart: ah good point

9:21 alinp: btw, how an agent can be shut down ?

9:21 is there a special way to do it ?

9:21 Chouser: all agents and pools can be shut down with 'shutdown-agents'

9:21 alinp: oh, ok

9:22 Chouser: they can't be restarted

9:22 alinp: but just one agent can't be closed ?

9:22 I know that in fact are java threads

9:22 and they can't be closed in a standard way

9:22 Chouser: no, not really.

9:22 cloudhead: I'm trying to use the clojure repl, but pressing the arrow keys don't work.. any ideas?

9:22 kotarak: alinp: no. The run only when sending to it.

9:23 Otherwise they are like an atom or ref.

9:23 slashus2: cloudhead: rlwrap

9:23 kotarak: They just take the storage space

9:23 Chouser: cloudhead: I recommend rlwrap

9:23 alinp: cloudhead: rlwrapp

9:23 :)

9:23 cloudhead: ahah

9:23 thanks guys

9:24 alinp: yw

9:40 technomancy: clojurebot: emacs is best configured for Clojure with instructions at http://technomancy.us/126

9:40 clojurebot: In Ordnung

9:43 kotarak: technomancy: did you just overwrite hiredman 's emacs factoids? He'll get grumpy...

9:43 cp2: technomancy: bla bla bla vim bla bla bla

9:44 technomancy: kotarak: I left "slime is icky" alone; I thought that was the main one he cared about.

9:45 if he cares about his factoids he should add confirmation for overwriting an existing one. =)

9:45 kotarak: technomancy: hehe going for compromise... ;)

9:55 hiredman: :(

9:56 technomancy: hiredman: he's your bot; do whatever you will

9:57 hiredman: didn't make it to this week's seafunc. =\ but I did find out Mark Engelberg lives in or around the north seattle area.

9:57 hiredman: been a busy week

9:58 technomancy: hiredman: oh I mean _i_ didn't make it

9:58 but yeah

9:58 also: I can't keep all the clj Marks straight.

9:59 hiredman: been a busy week means "I thought about it, but before coming to decision I forgot about it"

9:59 technomancy: that's what happens when meetings are scheduled whenever the organizer feels like it rather than on a regular basis

10:00 hiredman: I suppose

10:05 cemerick: I can't believe how bad the macros that I wrote last summer and fall are. :-(

10:05 kotarak: cemerick: that means, that you improved your macro-fu! :)

10:06 cemerick: heh, indeed. It's still sad to know I produced such craziness, though.

10:07 but yes, it's good to know there's been improvement. I think I've grown more in the last year (!!) of programming in clojure than in the prior 3 programming in Java, python, and scala.

10:09 kotarak: I have a similar experience. Clojure just makes fun again. :) Its consistency, the use of abstractions, its conciseness, the easy use of maps, sets, vectors.... Awesome, Awesome, Awesome, ... :D

10:10 cemerick: yeah. I discovered just how fast multimethods were last week. For the longest time, I would set up a cond form or something as long as I had less than a handful of conditions -- then I refactored what was a hotspot from a 4-form cond into a multimethod, and cut runtime of a particular piece down by 80% :-D

10:15 kotarak: cemerick: :)

10:38 ataggart: kotarak: yes I find myself giggling aloud a lot lately as I discover new ways of doing things with clj. my co-workers think I've gone batty.

10:39 cemerick: ~max

10:39 clojurebot: max people is 164

10:59 * Chouser has Programming Clojure sitting the shelf in his office.

10:59 * rodgertq is hoping his copy shows today

11:08 gnuvince: I'm getting Programming CLojure, SICP and Code Complete 2 this week

11:09 Computer Programming Nerd Nirvana :)

11:09 kotarak: gnuvince: I don't want to be you.... How do you decide where to start? ;)

11:15 Raynes: I never buy dead tree books.

11:15 gnuvince: kotarak: Code Complete is probably going to be my first step

11:16 kotarak: Programming Clojure I mostly read in PDF form and SICP, I want to wait until I get more maths under my belt

11:17 kotarak: I see... You are prepared. Or how was it? "Luck is where opportunity meets preparation"? Or "Luck favours the prepared"?

11:18 gnuvince: "Buying book is better when you have a job"

11:18 :)

11:18 In a few months, I'll be putting my career on hold for at least three years to get a B.Sc. in Computer Science

11:19 * hiredman found the original code complete at goodwill for $2

11:19 gnuvince: I figure I might as well buy the books I may want to read right now

11:19 rodgertq: still have my original code complete dead tree book

11:19 gnuvince: before I start feeding myself daily with ramen noodles

11:20 felzix: How can I turn a string into an integer?

11:20 gnuvince: Also got me a LaTeX reference and a math formulae reference

11:20 > (Integer/parseInt "3")

11:20 clojurebot: oy?

11:20 clojurebot: It's greek to me.

11:21 gnuvince: user=> (Integer/parseInt "3")

11:21 3

11:21 felzix: apparently the bot is not very cooperative this afternoon.

11:21 heading home

11:21 later

11:21 hiredman: ,(Integer/parseInt "3")

11:21 clojurebot: 3

11:21 felzix: thank you!

11:21 hiredman: ~works on my machine

11:21 clojurebot: http://haacked.com/images/haacked_com/WindowsLiveWriter/IConfigMapPathIsInaccessibleDueToItsProt_1446B/works-on-my-machine-starburst.png

11:22 gnuvince: hiredman: oh duh, > is lambdabot on #haskell :-/

11:22 Chouser: ,(Integer, "3") ; or this

11:22 clojurebot: java.lang.Exception: Expecting var, but Integer is mapped to class java.lang.Integer

11:22 Chouser: ,(Integer. "3") ; um, this

11:22 clojurebot: 3

11:23 durka42: ,(Integer/parseInt "3zoo" 36) ; but the constructor only does base 10

11:23 clojurebot: 186216

11:24 Chouser: ah

11:25 hiredman: ,36r3zoor

11:25 clojurebot: 6703803

11:27 felzix: ah, thanks

11:32 mebaran151: are there any good bindings for a BerkeleyDB like database for clojure

11:32 something like TokyoCabinet or QDBM would be perfect

11:33 Chouser: mebaran151: rhickey asked that very question just a couple days ago.

11:34 hiredman: http://justin.harmonize.fm/index.php/2009/03/fifth-static-storage-and-tokyo-cabinet/

11:34 dunno how well that works

11:34 cemerick: depending on your worldview, couchdb might fit. We're seeing good results using jcouchdb

11:34 mebaran151: hmm, maybe that would be a good task for me, porting moneta

11:34 dnolen: http://www.igvita.com/2009/02/13/tokyo-cabinet-beyond-key-value-store/

11:34 mebaran151: already tried CouchDB for this project: the doc model doesn't fit nearly as well as the BTree model

11:34 dnolen, yep

11:34 dnolen: talks about tokyo tyrant, which allows restfull access to tokyo cabinet

11:35 cemerick: ah, cabinet, which is local

11:35 mebaran151: Cabinet is much faster; seems like memcachedb would be the better hit if I wanted something like Tyrant

11:35 dnolen: technomancy is working on clojure httpclient lib. I've used it to interface with couchdb, works well enuf for me.

11:36 mebaran151: that could be a cool project for me in Clojure though, build a wrapper or at least an interface spec for all these key value stores

11:36 so you could just plug in Memcache or Tokyo Tyrant or Tokyo Cabinet and things would "just work"

11:37 dnolen: sounds like it, I haven't delved into it myself, but keeping my eye on it.

11:38 mebaran151: yeah key value stores are much easier to reason about than sql stuff: everything you query will be efficient and the algorithms are obvious

11:38 technomancy: mebaran151: if you don't mind a hosted solution, rhickey has an amazon simpledb lib that works great too.

11:38 mebaran151: I'm kind of scared of simpledb actually

11:39 I like to be able to break things on my own box before I start paying in production

11:39 hiredman: simpledb is pretty cheap

11:39 mebaran151: I really wish there were a free version of simpledb, maybe without all the whizbang scaling features, that I could use to test dev against

11:39 it is, but it's a principle thing...

11:39 hiredman: I think rhickey said his last simpledb bill was $0.04

11:40 mebaran151: everytime I run the test suite, I'd start to cry about the pennies

11:40 technomancy: mebaran151: there's a python simpledb-compatible server you can run locally to test against

11:40 no guarantee of compatibility, but it's worked OK for my initial explorations

11:40 http://simpledb-dev.googlecode.com/files/simpledb-dev-0.1.10.zip

11:41 dnolen: so what do people think about Google Wave? Looks like it'll be easy to code in Clojure.

11:44 mebaran151: what's it do?

11:44 Chouser: well, it's not released yet, so ... everything.

11:44 dnolen: fairly radical reinvention of distributed communication.

11:45 hiredman: *snort*

11:46 dnolen: short description: email that doesn't suck.

11:46 hiredman: uh

11:46 technomancy: it's more like a framework for building collaborative applications, from what I've seen

11:46 hiredman: looks like a framework for embedding little widgets in webpages

11:46 mebaran151: it's a bird, it's a plane, it's superapp!

11:46 dnolen: well not really

11:46 hiredman: hardly revolutionary

11:47 technomancy: mebaran151: don't forget a dessert topping and floor wax.

11:47 dnolen: it's protocol, and their opening the client and the server components.

11:47 mebaran151: it sounds like basecamp a little bit

11:47 hiredman: http://googlewavedev.blogspot.com/2009/05/introducing-google-wave-apis-what-can.html

11:47 dnolen: hiredman: you definitely missed the point, it's not about embedding widgets.

11:47 mebaran151: which was limitedly useful, but nothing beats scripting the hell out of .procmail

11:47 hiredman: it definitely is

11:48 durka42: but what's built in seems to be about combining email, instant messaging and twitter

11:48 dnolen: durka42: that's not what it is either.

11:48 mebaran151: didn't gmail mostly do that...

11:48 hiredman: I think you are confusing apps built with wave with wave

11:48 dnolen: on the web feeds are becoming popular: delicious, flickr, twitter, facebook.

11:48 clojurebot: delicious is http://delicious.com/clojurebot

11:48 dnolen: they're all reinventions of the same damn thing.

11:48 my email is a feed.

11:49 drewr: dnolen++

11:49 dnolen: a wave a generic feed.

11:49 is a generic.

11:49 hiredman: until they release something...

11:51 mebaran151: just another quick question: in a functional language like clojure, what's the best way of maintaining something super stateful like a database connection or io descriptor? In OO I would just pass around an object wrapping the database api, but in clojure, should I cleverly use a closure?

11:51 Chouser: mebaran151: I've been using maps for such things.

11:51 hiredman: mebaran151: take a look at clojure.contrib.sql

11:51 drewr: mebaran151: You still pass around an object.

11:52 mebaran151: but where do I keep the descriptor? (I haz no instance vars...)

11:53 drewr: I pass it as an argument to each function.

11:53 But, like Chouser said, I decorate it with a map so I can add metadata, etc.

11:53 Chouser: I've been using maps with keys like :socket, :stream, :handlers, etc.

11:54 I'm not yet sure if that's the best way, but it certainly works.

11:54 cp2: i was going to say something witty but i forgot what it was

11:54 drewr: Then I create macros around those functions that make working with the whole mess a lot simpler.

11:54 Chouser: the objects that are inherently mutable (like streams) are fine as values in the map

11:55 the values that are inherently immutable (like port/address of the socket) are fine as values as well

11:55 anything than needs to change (number of messages sent, or whatever), I use some kind of mutable reference for the value. Most often an atom, but refs make sense sometimes too, as do agents.

11:56 mebaran151: but couldn't I instantiate my get and set functions as a closure, that sort of absorbs this map, which itself would include the io descriptor?

11:56 so I could just say something like (get "key123")

11:56 hiredman: mebaran151: look at clojure.contrib.sql

11:57 mebaran151: or should I be using a macro for this sort of thing (i'm pretty new to LISP macros so I don't know where to use them yet)

11:57 hiredman, will do

11:57 Chouser: mebaran151: I haven't needed macros for such things yet

11:57 technomancy: mebaran151: rule 1 of macros: don't write macros

11:58 Chouser: mebaran151: but yes, a set of closures that close over your state are an option.

11:58 technomancy: rule 2 (for advanced usage): don't write macros _yet_

11:58 Chouser: rule 3: most of that macro you wrote belongs in a function

11:58 mebaran151: makes sense (I have enough trouble fighting emacs)

11:58 hiredman: mebaran151: the short answer is clojure has mechanisms for lexical and dynamic binding

11:59 lexical binding is at function creation time, and dynamic is at function call time

11:59 wwmorgan: #=(foo java.lang.String) : In the evaluation of this form, foo is passed a Symbol. Is there a way to make it so that foo is passed a Class instead?

11:59 hiredman: wwmorgan: use (Class/forName "String")

12:00 or whatever

12:00 wwmorgan: hiredman: hm. I think foo would be passed a list object in that case?

12:00 hiredman: mebaran151: having get close over the db connection is annoying because then you have to pass around the closure

12:01 and you have seperate sets of closures for each connection

12:01 Chouser: wwmorgan: you're playing with deep magic there, using #=(). Are you sure you want/need to?

12:01 hiredman: wwmorgan: inside foo, use Class/forName

12:02 mebaran151: hiredman, usually there should be only a finite number of connections, so that seems fine

12:02 technomancy: Chouser: I thought that was a typo; what _is_ it?

12:02 hiredman: mebaran151: did you look at that link I pasted about tokyo and clojure?

12:02 wwmorgan: Chouser: not at all :-). I'm looking at print-dup for serialization.

12:02 mebaran151: yep, but they used a nasty macro to preset the database

12:02 hiredman: uh

12:03 mebaran151: you misunderstood

12:03 a macro cannot represent a database

12:03 the macro is merely sugar over a binding form

12:03 Chouser: technomancy: #=() allows for evaluation at read-time (so before even macros kick in), but with odd rules.

12:04 technomancy: iiiinteresting

12:04 Chouser: ...like class names are not yet resolved.

12:04 wwmorgan: well, that's the one context where you may be justified in playing with #=(). :-)

12:05 hiredman: mebaran151: so the get function closes over *db* which is nil, but then you use binding to bind *db* to the database connection dynamically

12:05 Chouser: wwmorgan: in which case hiredman is right -- if you know that arg is a class name, and you need it to be a class, then something like (Class/forName arg) or (resolve arg) may be what you want.

12:06 mebaran151: I see

12:06 hiredman: mebaran151: which is not to say the code he ended up posting is all good

12:06 mebaran151: so essentially it's dynamic scope?

12:06 hiredman: $%@#

12:07 ...yes

12:07 Chouser: hiredman: deep breaths.

12:07 mebaran151: sorry!

12:07 hiredman: ,(doc binding)

12:07 clojurebot: "([bindings & body]); binding => var-symbol init-expr Creates new bindings for the (already-existing) vars, with the supplied initial values, executes the exprs in an implicit do, then re-establishes the bindings that existed before."

12:10 wwmorgan: Chouser: actually my use case requires calling into java library functions whose implementation I can't change. However, I can change the print-dup methods for our objects to not use #=. That's what I'll try next.

12:13 Chouser: wwmorgan: or have the contents of the #=() be different -- that can be adjusted with a defmethod I believe.

12:59 mrsolo: programming clojure shipped! woot

16:25 lgas: are there any good resources for best practices when designing programs/solution in clojure? e.g. I am trying to write a program to do automatic thesaurus generation based on frequency of words occuring together and one obvious approach would be to have a map with the key being word pairs and the value being an agent that gets incremented every time that pair is encountered but this is my first attempt to solve a real world concurr

16:25 problem in clojure so I have no basis for knowing if this is a good approach or not....

16:29 cmvkk: writing good concurrent programs is a pretty difficult problem

16:31 lgas: indeed

16:31 I can see how a lot of the concepts in clojure help make it more managable at a conceptual level, but putting the pieces together in practice is not as easy. For now I suppose I'll just try the approach that occured to me and see if it turns out to be good or not.

16:31 cmvkk: in that particular case, if all you're doing is gathering data about frequency of word pairs you'd probably be better off generating individual maps for sections of the input text concurrently, then merging them

16:32 but i'm not too sure either way

16:33 lgas: That makes sense. I'll try generating individual maps for each chunk of words I'm dealing with and then maybe send those to an agent to be merged into the master map...

16:52 slashus2: ,(time (+ (int 1) (int 1)))

16:52 clojurebot: 2

16:52 "Elapsed time: 0.147 msecs"

16:52 slashus2: ,(time (+ 1 (int 1)))

16:52 clojurebot: 2

16:52 "Elapsed time: 0.161 msecs"

16:53 slashus2: Must not be the latest revision. It seems to reflect when I did the first one on build 1381.

16:55 Wait this seems to be only with /

16:55 ,(time (/ (int 1) (int 1)))

16:55 clojurebot: 1

16:55 "Elapsed time: 0.556 msecs"

16:55 slashus2: ,(time (/ 1 (int 1)))

16:55 clojurebot: 1

16:55 "Elapsed time: 0.119 msecs"

16:56 cads: hey, do you guys think currying would be useful in clj, or would the strong presence of variadic functions just make it ackward to have to think about?

17:04 with fixed arity functions we can write (def add-five-things (curry (fn [x1 x2 x3 x4 x5] (+ x1 x2 x3 x4 x5))), then we can say (def add-three-things-to-ten (add-five-things 0 10)), and then (def add-one-thing-to-twelve (add-three-things-to-ten 1 1)), and so on, where the function yielded by a curry knows how to curry itself further until it's built up enough arguments to evaluate the whole function

17:05 I think those examples don't do much to motivate actually using curried fns though :)

17:06 hiredman: ,(doc partial)

17:06 clojurebot: "([f arg1] [f arg1 arg2] [f arg1 arg2 arg3] [f arg1 arg2 arg3 & more]); Takes a function f and fewer than the normal arguments to f, and returns a fn that takes a variable number of additional args. When called, the returned function calls f with args + additional args."

17:06 hiredman: I use it all the time

17:07 cads: there's a difference between currying and partial application: a partial application is a function that will finish a computation and yield a value in one shot of additional parameters.

17:07 While curry will yield a function when not given enough parameters to complete the original function, which will in turn take more params

17:08 cmvkk: i don't see the difference, other than that one is automatic I guess

17:08 cads: the problem with variadic functions and currying is that we don't know when we build a curry how many arguments the user will want to pass

17:08 hiredman: ,((uncurry + 1) 2)

17:08 clojurebot: java.lang.IllegalArgumentException: Wrong number of args passed to: sandbox$uncurry

17:09 hiredman: ,(((uncurry +) 1) 2)

17:09 clojurebot: #<sandbox$uncurry__2158$uc__2160 sandbox$uncurry__2158$uc__2160@163542c>

17:09 hiredman: ,((((uncurry +) 1) 2))

17:09 clojurebot: 3

17:09 cads: ,(doc uncurry)

17:09 clojurebot: "([x]); applied to a function it returns a function that will continue partially applying until it is called with no arguments"

17:09 cads: that's curry!

17:09 hiredman: ~transform

17:09 clojurebot: transform is http://github.com/hiredman/odds-and-ends/blob/8a84e6ddbad9d71f714ba16c3e1239633228a7eb/functional.clj

17:09 hiredman: cads: yeah well, I wasn't paying too close attention

17:09 cads: wait, no it's not, it's a kind of curry

17:10 cmvkk: in any case, having automatic currying doesn't work with multiple-arity functions very well

17:10 cads: yeah, in the above case we must wrap our result in an extra set of parens

17:10 hiredman: ,(pl ?+ $ 1 $ 2 $ 3 $ nil)

17:10 clojurebot: java.lang.ClassCastException: java.lang.Integer cannot be cast to clojure.lang.IFn

17:11 hiredman: bah

17:11 chessguy: and it tends to make for messy error messages with known-arity functions too, cmvkk

17:11 (a la haskell)

17:11 slashus2: hiredman: I thought at first that the reflection was because the new reduce function wasn't overloaded to include Integer, but that doesn't seem to be the case.

17:11 hiredman: slashus2: huh?

17:12 cads: in my implementation thing, for variadics we have to say something like ((((curry * 3) 2) 4) 2 1 :end) => 48

17:12 slashus2: hiredman: In revision 1381, it seems that something like (/ (int 1) (int 2)) will reflect when you activate *warn-on-reflection*

17:12 cads: and I am hard pressed to think of fixed arity functions with more than 2 parameters in clojure, everything variadic :)

17:12 slashus2: Where that wouldn't occur before.

17:13 ,(time (/ (int 1) (int 2)))

17:13 clojurebot: 1/2

17:13 "Elapsed time: 0.458 msecs"

17:13 hiredman: ~latest

17:13 clojurebot: latest is 1381

17:13 hiredman: r1381

17:13 r1380

17:13 cads: hey chessguy

17:14 chessguy: hi

17:14 hiredman: ,(macroexpand '(pl ?+ $ 1 $ 2 $ 3 $ nil))

17:14 clojurebot: (do ((uncurry +) (1 (2 (3 nil)))))

17:14 hiredman: hmmm

17:15 ,(pl (reduce ?+ '(1 2 3)))

17:15 clojurebot: #<sandbox$uncurry__2158$uc__2160 sandbox$uncurry__2158$uc__2160@3fa50b>

17:15 hiredman: ,(pl ((reduce ?+ '(1 2 3))))

17:15 clojurebot: java.lang.ClassCastException: sandbox$uncurry__2158$uc__2160 cannot be cast to java.lang.Number

17:16 hiredman: slashus2: sorry I dunno

17:17 slashus2: I am trying to figure it out, but my limited understanding of when clojure decides to reflect makes it difficult.

17:27 hiredman: It seems to only do it when you use (int 5) replace 5 with any number.

17:27 ,(class (int 5))

17:27 clojurebot: java.lang.Integer

17:28 chessguy: ,(doc int)

17:28 clojurebot: "([x]); Coerce to int"

17:36 slashus2: Doesn't seem to do it with (/ (Integer. 5) (Integer. 5))

17:42 Chouser: slashus2: are you sure the reflection behavior of (/ (int 5) (int 5)) has changed?

17:43 seems to warn in 1.0.0 as well

17:43 slashus2: You are correct.

17:43 Chouser: I think the issue is that there's no divide method for int,int

17:43 slashus2: I was getting ready to check back to 1376

17:43 Chouser: I guess if you want integer division you can use 'quot'

17:44 slashus2: :-/

17:44 Chouser: (show clojure.lang.Numbers "divide")

17:44 slashus2: It seems that (/ (int 1) (int 2)) would be faster than (/ 1 (int 2))

17:44 Misconception I guess.

17:45 Chouser: there is also unchecked-dividie

17:45 unchecked-divide

17:46 hiredman: Chouser: rhickey seems to think going to unchecked-whatever is a bad idea unless you specificly what it's behaviour (truncating, etc)

17:46 Chouser: yeah

17:47 quot doesn't use primitives -- it boxes, so that's slow

17:47 slashus2: Same thing exists with 1376 as well. I guess I was mistaken.

17:48 Chouser: (unchecked-divide (int 5) (int 5)) is not quite twice the speed of (/ (int 5) (float 5))

17:48 slashus2: Are we going to create a divide method with int int

17:49 Chouser: we?

17:49 slashus2: The community?

17:49 Chouser: or a quot that takes primitives?

17:50 hiredman: or build a clojure machine out of a fpga

17:51 slashus2: It is strange that (/ 1 2) is about 5 times slower than (/ (int 1) (int 2))

17:52 Chouser: not that strange. boxing is slow.

17:52 oh

17:52 slashus2: Chouser I meant it the other way around.

17:52 sorry

17:52 Chouser: yeah, I was about to correct myself

17:53 boxing is quite a bit slower than primitive math, but reflection is *really* slow.

17:53 compared to compiled direct calls.

17:55 slashus2: I guess this is expected behavior? So make sure that both of the arguments are not coerced?

17:55 Chouser: so, for now your best route (if you're dealing with primitives already, anyway) is probably to coerce one of your ints to a float or something so that you can get a primitive call

17:56 slashus2: Not something to be fixed?

17:56 Chouser: next best would be to use quot -- that gives clearly defined behavior and avoids reflection, so only costs you the boxing.

17:57 oh, you could certainly raise the issue. I don't do enough math stuff to know what's best, but rhickey certainly cares about numeric performance.

17:57 slashus2: Someone raised it in the group, but it has no attention yet.

17:57 Their observation that it was different in 1376 was incorrect.. I think.

17:58 Chouser: the two solutions that seem most likely would be either int,int and long,long etc. divide methods, or primitive quot methods.

17:58 slashus2: Chouser: Yes, I just added an int int method and it now works with the same performance.

17:58 arohner: is there a function that works like str-join, but for collections?

17:58 Chouser: nice

17:59 slashus2: I wish I had a CA turned in.

17:59 arohner: i.e. (join [1 3 5] :a) => (1 :a 3 :a 5) ?

17:59 Chouser: ,(interpose 'x (range 5))

17:59 clojurebot: (0 x 1 x 2 x 3 x 4)

17:59 hiredman: clojurebot: do you have any life lesson advice?

17:59 clojurebot: live every week, like it is shark week!

17:59 arohner: Chouser: thanks!

18:00 Chouser: arohner: do you use repl-utils? (source str-join)

18:00 arohner: ah, that's a good idea

18:00 I did find-doc before asking the channel :-)

18:01 Chouser: I mean, feel free to ask anytime, but I thought I'd mention it.

18:01 arohner: it's good advice, thanks

18:06 slashus2: Chouser: http://groups.google.com/group/clojure/browse_thread/thread/a767baaa0b3b17eb is where the issue was brought up. I replied with as clear of explanation as possible.

18:07 I think that is the problem he was having here, but I could be mistaken.

18:08 Chouser: hm, I didn't realize 'count' had an inline definition, so it can "return" a primitive.

18:09 slashus2: I guess that is what happened.. It returned an int primitive.

18:10 This problem could be causing quite a bit of performance loss in people's code who don't expect this behavior.

18:10 Chouser: anyway, in that case where he's dealing largely with boxed things and integers anyway, he should probably use quot

18:12 slashus2: ,(time (/ 5 5))

18:12 clojurebot: 1

18:12 "Elapsed time: 0.114 msecs"

18:12 slashus2: ,(time (quot 5 5))

18:12 clojurebot: 1

18:12 Chouser: but you're right, what changed was 'count' got an inline definition

18:12 clojurebot: "Elapsed time: 0.149 msecs"

18:12 slashus2: Ahh, that was the change that I was looking for.

18:12 Makes sense now.

18:22 Chouser: the most fun thing about jna is errors don't result in wimpy little stack traces and a new repl prompt. Instead you get full-on segfaults.

19:34 lgas: I'm trying to get used to working with clojure in emacs with slime... is there a way to define namespaces/uses/imports/refers/etc such that you can just slime-eval-buffer (or do it an expression at a time or whatever) to get everything in your source file evaluated into the repl? Right now I have problems where I have (ns .. (:use .. (..))) declarations that I have to repeat manually with just (use '..) to get them in scope in the

19:34 repl...?

19:43 durka42: i'm confused

19:43 when you eval a buffer with (ns ...) in it, it doesn't work?

19:45 lgas: yes, it gives me a "Unable to resolve symbol: re-split" (for example) even though in my (ns ..) I am :use'ing clojure.contrib.str-utils.

19:47 durka42: for re-split that's used in the buffer?

19:47 arohner: lgas: how are you loading the buffer in slime?

19:47 durka42: or that's used in the REPL after eval-buffer'ing?

19:47 if the latter, you need to in-ns in the REPL

19:47 arohner: I use C-c C-l <enter>

19:48 i.e slime load file rather than slime eval buffer

19:50 lgas: Yeah, C-c C-l <enter> on this file: http://pastie.org/493608 gives me the "Unable to resolve symbol: re-split" error.

19:51 but if I manually (in-ns 'wordformgen) and then (use 'clojure.contrib.str-utils) it's fine

19:51 arohner: hmm...

19:51 see if

19:52 (ns wordformgen (;use clojure.contrib.str-utils) (:use clojure.contrib.combinatorics)) works

19:53 lgas: I assume that ; should be a : ?

19:54 arohner: yes, sorry

19:54 lgas: I think that worked... though I have another error now, so let me see

19:55 ok yeah that got it

19:56 thanks a bunch

19:57 arohner: np

20:37 cads: hey hiredman, I like your functional.clj

20:38 I think it's time I actually played around with a zip structure, because those methods look neat

20:47 chewy: in swank-clojure what's the difference between doing a load (c-c c-l) and a compile (c-c c-k)?

20:51 vy: chewy: I'm not sure but there shouldn't be a difference, since Clojure is directly compiling the snippets to byte code, no difference between interpretation and compilation, AFAIK.

20:53 lgas: why would (count (line-seq rdr)) work but (map println (line-seq rdr)) throw a "Stream closed" IOException (and yes, I'm using new readers for both tests)

20:54 durka42: ~map

20:54 clojurebot: map is *LAZY*

20:55 lgas: ok... why would map being lazy cause the IOException? I would expect the laziness to just cause nothing to happen until it was forced...

20:55 durka42: exactly. and the stream is gone by then

20:55 lgas: ah ok I get it

20:55 thanks

20:56 durka42: you can wrap a doall, or use doseq instead of map

20:56 ~def line-seq

20:58 hm, line-seq is also lazy

21:02 lgas: well, in my case, I was calling a function that had the (with-open ... (map (lazy-seq ..))) from the repl, so I guess that was forcing the map which was forcing the lazy seq, but it was forcing it after the stream was already gone

22:40 jdz: gz on the book, Stuart

22:40 !

23:25 djpowell: Is there any way to get the class name of the implementation of a function from inside that function?

23:29 I guess I can do it by using (def (fn thisfn [ ...

23:36 hoeck: djpowell: or just (defn me [] (type me))

Logging service provided by n01se.net