#clojure log - Apr 27 2009

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

0:00 st3f4n: yeah but it is like decoding g++ error messages now :)

0:28 bradford: hi clojurians, i have a test-is test that yearns to have side-effecting operations (setup and teardown)

0:28 what is the best way to do this?

0:28 right now, i do this:

0:28 (defmacro def-hbase-test [name table-name column-families test]

0:28 `(deftest ~name

0:28 (let [blah# (create-hbase-table ~table-name ~column-families)

0:28 result# ~test

0:28 bleh# (disable-table ~table-name) blek# (drop-hbase-table ~table-name)] result#)))

0:29 the let binding here feels a bit horrid

0:45 technomancy: st3f4n: (if [...] (do is used to make the point that "do" means side-effects are involved

0:45 bradford: test-is has a new "fixtures" feature that does that

0:45 it's documented pretty well in the source; not hard to use

0:46 st3f4n: the typo with quote(a b c) was unfortunate though. =\

1:23 unlink1: How do I "chain" functions, where one takes the result of the previous as its arg?

1:24 hiredman: (doc comp)

1:24 clojurebot: Takes a set of functions and returns a fn that is the composition of those fns. The returned fn takes a variable number of args, applies the rightmost of fns to the args, the next fn (right-to-left) to the result, etc.; arglists ([& fs])

1:24 hiredman: ,((comp first rest) (range 10))

1:24 clojurebot: 1

1:24 unlink1: http://paste.lisp.org/display/79271

1:24 hiredman: ,(-> (range 10) rest first)

1:24 clojurebot: 1

1:25 unlink1: Or is it idiomatic to have a big let like that?

1:25 hiredman: use comp

1:26 (. (File. dir) listFiles)

1:26 should be

1:26 (.listFile (File. dir))

1:26 unlink1: thanks

1:31 hiredman: bah

1:31 http://paste.lisp.org/display/79271#1

1:31 where is the lisp bot?

1:32 lisppaste8: well?

1:32 unlink1: agh, I want currying

2:32 hiredman: http://www.flickr.com/photos/90204102@N00/3454823876/in/set-72157616928718721/

3:02 bOR_: Hmm. our vimclojure creator isn't around yet, is he? Got it compiled, but still an odd error when opening vim.

3:02 invalid argument: 1

3:02 in function! vimclojure#PreviewWindow.goHere() dict

3:03 and invalid argument: 4

3:03 in function! vimclojure#PreviewWindow.close() dict

4:18 AWizzArd: bOR_: kotarak is now here. You should repeat what you said about VimClojure.

4:28 bOR_: ah, thanks.

4:29 Hmm. our vimclojure creator isn't around yet, is he? Got it compiled, but still an odd error when opening vim.

4:29 09:02 bOR_ invalid argument: 1

4:29 bOR_ in function! vimclojure#PreviewWindow.goHere() dict

4:29 bOR_ and invalid argument: 4

4:29 bOR_ in function! vimclojure#PreviewWindow.close() dict

4:29 kotarak: bOR_: What are you doing when this error arises.

4:29 bOR_: vim newfile.clj

4:29 opening an empty file.

4:30 started the ng server, typed vim newfile.clj

4:31 I did get the ant thing to work though ( the previous problem).. and both on my other work and at home, vimclojure is working fine.

4:32 kotarak: hmmm

4:33 bOR_: is there any way I can screw up in starting the ng server that causes this? I copied all the compiled jars to a single directory, and starting ng server by pointing to the jars in that directory.

4:34 brb. getting a coffee :).

4:35 kotarak: bOR_: that should work. I'm guessing, that the numbers are window numbers which don't exist for some reason. But opening a new file shouldn't open the preview window, though...

4:37 bOR_: is that something I could trigger from my .vimrc?

4:37 set nocompatible

4:37 filetype plugin indent on

4:37 syntax on

4:37 let maplocalleader = ","

4:37 let clj_highlight_builtime = 1

4:37 let clj_highlight_contrib = 1

4:37 let clj_paren_rainbow = 0

4:37 let clj_want_gorilla = 1

4:37 let vimclojure#NailgunClient = "/home/boris/.local/src/vimclojure-2.1.0/ng"

4:37 set number

4:37 set hidden

4:37 set showmode

4:38 kotarak: No. But the "builtime" should probably read "builtin"

4:38 bOR_: ohh.

4:39 hehe.

4:39 spotted that as well.

4:39 kotarak: Otherwise it looks ok.

4:40 Hmmm. Which version are you using?

4:40 bOR_: doesn't affect the error message.

4:41 7.0.237 (vim), 2.1.0 vimclojure

4:42 ah. never tried beyond that first error message. I do get the ,sr to work now though.

4:42 hmm. when I do a ,eb from some code, I do get the preview window, but with an error message blinking by.

4:43 kotarak: 7.0 is ooold. But I'm not sure this has an effect. Tracking in which version a feature was added in Vim is harder than flying through a black hole and surviving the trip...

4:43 bOR_: Error detected while processing function vimclojure#EvalBlock..3..1:

4:43 line 1:

4:43 E716: Key not present in Dictionary: _buffer

4:43 E15: Invalid expression: "buffer! " . self._buffer

4:43 kotarak: bOR_: you can use :messages to review the error

4:43 bOR_: *nod*

4:43 I remembered that.

4:45 kotarak: Something is messed up, setting up the buffer information.

4:45 Just a sec.

4:50 bOR_: For the moment I'm suspecting that your vim is simply to old. Can you check it has the copy() function.

4:51 bOR_: will check.

4:51 do you know by heart how to check that? otherwise I'll look it up.

4:54 ah, using call.

4:55 :call copy()

4:55 gives 'not enough arguments for function: copy'

4:57 :call copy(1) works fine.

4:57 i'll install a newer vim and see if that helps, and report back.

4:58 then you might just add a warning somewhere saying that vim 7.0.237 and ant < 6.5 give trouble :).

5:05 new vim 7.2 works like a charm.

5:05 kotarak: bOR_: I can guarantee 7.2.108 as MacVim to work. :)

5:06 Ok :)

5:06 Will add a note.

5:06 bOR_: great :). thanks for vimclojure :)

5:06 kotarak: bOR_: np :)

5:09 bOR_: not sure if you want to hear this, but an empty file, with (+ 3 1) and ,el gives an error message. ,et works though

5:09 ;)

5:09 Error detected while processing function vimclojure#EvalLine..vimclojure#ExecuteNailWithInput:

5:09 line 9:

5:09 E686: Argument of writefile() must be a List

5:10 kotarak: Woops. I see it here, too. Will check!

5:12 bOR_; Thanks for the report, will fix it.

5:28 bOR_: EvalLine is fixed. There was an ominous -1 behind the getline() call. The fix is pushed to the default branch of the repository.

7:48 samuels: wow it's a fight between clojure and scala in chan popularity

7:48 almost neck and neck

8:40 AWizzArd: ,(class (ref 10))

8:40 clojurebot: clojure.lang.Ref

8:40 AWizzArd: ,(instance? clojure.lang.Ref (class (ref 10)))

8:40 clojurebot: false

8:41 AWizzArd: ,(instance? Number 25)

8:41 clojurebot: true

8:41 AWizzArd: Uh, what am I missing here?

8:41 hoeck: ,(type (class (ref 10)))

8:41 clojurebot: java.lang.Class

8:41 kotarak: ,(instance? clojure.lang.Ref (ref 10))

8:41 clojurebot: true

8:42 AWizzArd: ah, danke

9:46 rhickey: Hi. I wanted to ask if you could maybe add a def- to Clojure, which automatically inserts #^{:private true ..}.

9:46 Chouser: AWizzArd: you're aware of contrib.def ?

9:47 rhickey: AWizzArd: not before 1.0

9:48 AWizzArd: Chouser: Contrib changes too fast for me to track everything. I remember there was something like defvar. Anyway, thanks for the tip.

9:48 rhickey: Ok, thanks.

9:49 rhickey: everyone ready to switch to hg?

9:49 * rhickey ducks

9:49 AWizzArd: WOW :)

9:49 Very ready

9:50 If you should decide to switch to hg I think kotarak will make three big crosses in his calendar ;)

9:50 rhickey: does anyone have any experience with bitbucket?

9:51 AWizzArd: I think kotarak has experience with it. He'll be back soon. ( http://bitbucket.org/kotarak/ )

9:53 rhickey: how about IntelliJ w/Mercurial?

9:53 gnuvince: rhickey: I use BitBucket, but have no experience with any IDE's integration with Mercurial.

9:54 AWizzArd: Do you develop Clojure in IntelliJ?

9:54 rhickey: AWizzArd: the Java bits, yes

9:55 plus I do all my SVN stuff, patches, diffs etc through IntelliJ

9:55 AWizzArd: I have some experience with NetBeans + Mercurial. Haven't seen IntelliJ so far.

9:59 http://plugins.intellij.net/plugin/?id=2038 seems to say that there is such a plugin. But the comments are not so nice..

10:00 rhickey: AWizzArd: there's no substitute for official support

10:01 AWizzArd: right

10:02 cemerick: hrm, I guess I better start hoping for a git-hg :-/

10:02 Netbeans' hg support is hopelessly bad, IMO

10:02 MikeSeth: tee hee

10:02 Chouser: cemerick: I assumed git-hg existed! it doesn't?

10:02 AWizzArd: cemerick: why do you think so? I found it to be very good.

10:03 Excellent visual representation, all important features are included...

10:03 cemerick: Chouser: I dunno -- `git hg' does nothing, and searching for git-hg yields no man page...

10:04 Chouser: well, I guess I could re-learn hg

10:04 eevar2: http://kensipe.blogspot.com/2009/02/intellij-81-with-git-support.html :p

10:05 AWizzArd: I found that very easy.. learning hg I mean.

10:05 cemerick: AWizzArd: it's painfully slow, even slower than NB's svn support.

10:05 AWizzArd: Hmm, I have not experienced that. I click on "diff" and this very advanced visual support opens. On my system it is very fast.

10:06 drewr: One would think git-hg would be less impedence mismatch than git-svn.

10:09 rhickey: Still not quite there with: 1) Viable free hosting service with good user management, issues + wiki, 2) using a VCS with support in 3) my main IDE, and 4) in a good commercial hosting service with private plans for my commercial dev, those plans having multiple private repos per project once DVCS (for main/release/staging branches)

10:09 Chouser: that all exists for svn? wow.

10:10 eevar2: github does 1, 2 & 4, while my link above solves 3

10:10 rhickey: So far 2)git 3)Intellij, 4)unfuddle leaves me out of luck on googlecode, so far github doesn't do it for me, their pages always tie up my browser

10:10 eevar2: ^^ plug plug ;)

10:11 rhickey: 1) googlecode, 2)hg, 4)bitbucket leaves me hurting for 3, and bitbucket delivers a lot less than unfuddle/assembla

10:12 drewr: There is definitely some errant JS somewhere on github.com. FF spins up my fan every time I leave one of those pages open.

10:12 rhickey: drewr: yup

10:12 AWizzArd: rhickey: And using NetBeans instead of IntelliJ is no option?

10:13 * rhickey happily pays for IntelliJ and has for years, no better IMO

10:13 rhickey: none better

10:14 AWizzArd: And when having in mind that Clojure will "one day" be rewritten in Clojure, would then IntelliJ and NetBeans be on par?

10:14 I could think that the devs concentrated on Java productivity so far (for both camps).

10:17 rhickey: are there good visual tools (esp merging and repository viewing) for hg on OS X? (Netbeans as an hg frontend seems like overkill)

10:18 cemerick: rhickey: there's hgk, which is a tk frontend (might be good or bad depending on your perspective)

10:18 rsynnott: github does tend to be TERRIBLY slow

10:19 cemerick: it and gitk share some murky past, and they're roughly equivalent as far as I remember

10:19 I much prefer gitx. http://gitx.frim.nl/seeit.html Though, it's obviously moot if hg is the direction.

10:20 rhickey: cemerick: if git I'll still have intellij support

10:21 git's bad windows story is also a problem for commercial work

10:21 AWizzArd: The visual support for hg under Windows is very nice with TortoiseHg and NetBeans.

10:21 cemerick: rhickey: FWIW, we use it on windows successfully *shrug*

10:21 gnuvince: As far as I know, the Mercurial support is still not public on Google Code, is it?

10:22 hoeck: using git here to, on win2k

10:22 AWizzArd: We used git on Windows, before we switched to hg.

10:22 hoeck: s/to/too

10:22 cemerick: if the windows port ever falls down, we can revert to having windows builds done on a share served by a linux box

10:22 haven't had to do that yet, but it's there just in case

10:25 rhickey: cemerick: still, the need for that, and lack of support out of the box is a bad smell to the ideology of git, IMO. I hate C programs, shell crap etc. Tools like this should be written in portable HLLs like Python/Java, have good HTTP interfaces etc. The 1970s are over

10:28 Chouser: hg's you're only fit there, isn't it? SVN is mostly C, isn't it?

10:28 cemerick: rhickey: generally, I'd agree. There's so much ideology in that camp that I almost discounted git out of hand, because I didn't want to have to deal with it if we had a problem (same dynamic as here vs. c.l.l., etc). But our evaluation of hg vs. git came out so overwhelmingly in favor of git that I had to bury my *ick* reaction.

10:28 * drewr likes shell crap

10:29 rhickey: Chouser: svn is old, git is new. Why linux people still insist on building apps with systems tool is beyond me.

10:30 Chouser: well, "linux kernel people" I think answers your question, though not in a way that may be satisfactory to you. Or me.

10:30 rhickey: cemerick: That's interesting - what do you think of google's analysis: http://code.google.com/p/support/wiki/DVCSAnalysis

10:30 Chouser: that's true for git, but what about all the other stuff written in gcc, all the UI stuff etc?

10:31 drewr: cemerick: Pretty much the same here. Initially chose hg because of its simpler interface/concepts/etc., but eventually the power of git won me over.

10:31 clojurebot: kotarak prefers hg

10:31 AWizzArd: cemerick: We started out with git first. But we happily switched to hg after two months of using it. It's easier to use and we don't miss any feature. With its patch queues and rebasing feature hg pretty much offers a very complete package. Anyway, this is going a bit too flamewarish ;-)

10:31 rhickey: AWizzArd: no one's flaming yet

10:31 AWizzArd: Right. I just can smell it *wink*

10:32 clojurebot: botsnack

10:32 clojurebot: thanks; that was delicious. (nom nom nom)

10:32 rsynnott: ah, hg's in python, of course

10:32 no wonder google went for it

10:34 cemerick: rhickey: I saw that last week. I don't have any requirements to host repos via http, which sounds like was the big deal-breaker for them. I disagree with it on a number of fronts: being able to rewrite history is good (for us), in-place branches are *essential* IMO, per-file rename tracking reminds me of cvs.

10:34 AWizzArd: rsynnott: I think the Dictator of Python works for google as well.

10:34 Chouser: I use "git svn rebase" all the time for my git-svn repos. ...especially clojure, since I can't commit and want to provide clean, recent patches.

10:34 gnuvince: IMO, Mercurial has this going for it: 1) Command set is practically a carbon copy of Subversion's, 2) The documentation is installed by default, complete and does not use man, 3) More thank quick enough for all my projects; I don't know about Clojure, but if it's good enough for Mozilla, I figure we're safe. 4) Easy to write extensions, 5) lower learning curve than Git (yes it does, Git people), 6) Google Code support.

10:35 AWizzArd: Chouser: I use "hg pull --rebase" quite often.

10:35 cemerick: it's clear that hg and git hit different people's sweet-spots, so some kind of interop is going to be necessary on both ends (and is already there for git -> hg, I think).

10:35 Chouser: AWizzArd: ah, I see that's new. cool.

10:36 AWizzArd: gnuvince: mainly point 5) made us switch from git to hg.

10:37 cemerick: AWizzArd: That always confuses me. SCM, no matter what flavor, is certainly simpler than any of a dozen different technologies/concepts that programmers need to understand back-to-front. Why then is 'learning curve' a big differentiator there?

10:37 Chouser: I agree that git's support for renaming is bizzare.

10:38 marklar: cemerick: I've always thought the same thing

10:39 Chouser: But I love git's inside-repo branches. *sigh*

10:39 gnuvince: I don't understand the love for that feature

10:40 cemerick: Chouser: git doesn't automagically detect your renames?

10:40 Chouser: I use it all the time. I create and remove branches easily without copying whole working copy checkouts.

10:40 cemerick: gnuvince: having a pile of working copies is really painful, IMO

10:40 Chouser: cemerick: it detects rename at log-view time, or diff-time, instead of at commit.

10:41 cemerick: in place branches + stashes is quite wonderful

10:41 AWizzArd: Chouser: did you try Mercurials patch-queues?

10:41 Chouser: AWizzArd: no, I haven't.

10:41 rhickey: Chouser: yes, you just toggle the version you are working on in place right, rather than switch to a different dir?

10:41 cemerick: Chouser: that always made sense to me *shrug*

10:41 gnuvince: cemerick: I just think that opaque working copies are not really a benefit

10:41 Chouser: rhickey: yes.

10:42 AWizzArd: rhickey: a propos flamewar - did the creator of this channel give you some administration rights? I mean, just in case...

10:42 rhickey: in hg I'll need multiple dirs? do queues mitigate this at al? http://hgbook.red-bean.com/read/managing-change-with-mercurial-queues.html

10:43 AWizzArd: I'm trying to understand the diffs here, gathering opinions from people I respect, no war is happening

10:43 cemerick: rhickey: it's actually even a little better than that. You can work for a while, 'stash' your changes associated with a particular branch (*not* commit them), checkout a different branch, work, commit, and then go back to your original branch, apply what you previously stashed, and be back on your way.

10:44 AWizzArd: rhickey: right, it's going good so far. But in general, if some spammers join or people who just want to cause trouble, can we remove them?

10:46 The patch queues are nice for example for experimenting with patches. You can have patches A, B, C, D and E. Now you want to see how the current code base works with A and B. Or with A, C and E, or any other combination. One can put patches into the queue, apply some of them, put them back, etc.

10:46 rhickey: cemerick: to the complexity point, I think devs need to push back against inessential complexity at every turn. When I was younger I thought it was a form of power to know all these ridiculous details (tools/APIs), now I want none of it, let me work on something that matters already

10:47 cemerick: I'm halfway through my git book, that stashing is adding to index w/o commit?

10:48 Chouser: I think stashing is more like a commit to a special little branch

10:49 AWizzArd: it is comparable to Mercurials shelve

10:49 Chouser: but the commands are used differently. "git stash" takes all your working copy changes, commits them to the stash branch, and the rolls back your working copy.

10:49 rhickey: how do you see what is going on? I hope not just with command-line tools that spew text

10:49 Chouser: now you can apply that stashed patch to either the current branch, or any other you switch to.

10:49 cemerick: rhickey: Absolutely. I guess I'm positing that there is often inherent complexity when it comes to SCM, and using a system that is conceptually simpler than the problems you have will likely lead to difficulty down the road.

10:50 * Chouser likes command-line tools that spew text. Sorry.

10:51 cemerick: rhickey: I don't know the details of how stashes are implemented.

10:51 noidi: git stash is what you use when you're in the middle of some change, but then have to do something that requires you to have a clean copy of the sources

10:51 Chouser: "git stash list" shows changes you've stashed. "git branch" shows all your repo's branches, and which one you're on.

10:51 noidi: i.e. when what you have is too broken/unfinished to be commited, but you've done enough work that you don't want to lose it

10:51 Chouser: ...plus my bash prompt shows me my current branch name

10:52 rhickey: one q I have about all of these is where do your stashes/microbranches etc go? I want them all up on a host, not on my local drive

10:52 cemerick: rhickey: FWIW, I (in general) despise command-line tools as well. Thankfully, gitx allows me to eschew them for the most part, although stashing and such are still on the command line.

10:52 * AWizzArd doesn't like that all branches are kept in the repo.

10:53 cemerick: rhickey: stashes are local-only, I believe. All branches (by default) will get pushed to your remote when you 'git push'

10:53 noidi: a simple clojure question: can I make :use import everthing that the :used namespace has imported?

10:53 rhickey: I routinely work on 3 different machines, local drives are my nemesis

10:53 * rhickey wants all my stuff in the cloud

10:54 cemerick: noidi: I don't think so, no.

10:56 noidi: is there a reason why the imported java symbols are handled differently from the symbols defined in the namespace?

10:57 Chouser: I generally keep changes stashed for no more than a couple minutes at a time. As cemerick said, all regular in-repo branches are pushed by default.

10:57 cemerick: rhickey: yeah, stashes do not get cloned, so if you wanted to avoid storing anything locally, you'd create and commit to a temporary branch and then merge it back in when you're ready.

10:58 Ugh, that reminds me of what really killed me w/ hg -- can't delete branches or tags (maybe that's changed?)

10:59 drewr: hg has named, in-repo branches, but they're cumbersome.

10:59 clojurebot: kotarak prefers hg

10:59 noidi: what's the best way to deal with repeating sets of imports? I was hoping I could create a module that does the importing and the :use it, but apparently that's not possible :P

10:59 *then

10:59 drewr: Why is clojurebot only tormenting me?

11:01 cemerick: noidi: you could create a fn that does your imports, and then have a top-level invocation of that fn in whatever other namespace needed them.

11:01 noidi: cemerick, ok, I'll do that, thanks

11:02 cemerick: not super-clean, but it'd work. I don't often need very many imports, and when I do, I only need them in one ns.

11:02 chessguy_work: clojurebot: be nice

11:02 clojurebot: Gabh mo leithsc�al?

11:02 rhickey: so, with git/hg is there a simple way to say - store whatever I'm doing (complete or not) on the server and let me go home, pull down, and continue? for hg will I have to do that in every branch dir?

11:03 that's the #1 feature I'm looking for

11:03 Chouser: in git that would be "git commit -a; git push"

11:04 rhickey: Chouser: what if I'm not ready to commit?

11:04 noidi: you can commit into a temporary branch

11:04 Chouser: you'd end up with intermediate commits, so you wouldn't want to be pushing to the public main repo

11:04 noidi: or you can turn that commit into a patch and then remove the changeset from your repo

11:04 rhickey: that's ok

11:05 cemerick: a temporary branch is a 'git checkout' away, too

11:05 rhickey: cemerick: saw that

11:05 and for hg?

11:05 cemerick: that's what I'd do, just so that whoever's tracking master doesn't get confused.

11:05 Chouser: well, you can't have multiple bit branchs in an uncommitted state, just the current one

11:05 noidi: actually that's the case where I most often use stash

11:05 Chouser: s/bit/git/

11:05 rhickey: presuming multiple branches with different features/experiments/fixes in progress?

11:06 noidi: "oh man, I should've done this change in its own branch. git stash; git checkout -b newbrach; git pop"

11:06 Chouser: noidi: yeah, I do that.

11:06 noidi: git stash pop

11:06 rhickey: Chouser: I don't understand, can;t have multiple branches uncommitted ?

11:07 cemerick: rhickey: same workflow, your temp branches would just be rooted by something other than master.

11:07 rhickey: cemerick: a single root?

11:08 cemerick: rhickey: or multiple, totally depends on what strains of development you have going on

11:08 Chouser: all your branches are at a committed state, plus you have your working-copy changes on top of your current branch. If you want to set aside those changes and that branch to work on some other branch, I think you have to commit your current working-copy changes before you switch.

11:09 rhickey: cemerick: multiple independent strains

11:09 Chouser: or what happens?

11:10 AWizzArd: In Mercurial you would probably want to have one directory for each real branch, and then work in those. Mercurial supports hard links, to not waste space.

11:10 cemerick: rhickey: your WC changes get carried with you to the branch you're switching to. Remember though, there's zero cost to creating a temp branch and committing to it.

11:10 rhickey: cemerick: I'm trying to tease out if git's in-repo branches are needed for easy put-up-everything workflow

11:10 AWizzArd: When you are done with work just commit your changes in the middle of your work and click on "push".

11:11 cemerick: rhickey: ah, I see. They certainly make things much, much easier, IMO. cd-ing back and forth to different clones (branches in hg's parlance) gets tiring really quickly (again, IMO).

11:11 rhickey: cemerick: that sounds like a dangerous mistake possibly (change w/o commit)?

11:12 cemerick: rhickey: same behaviour as svn, interestingly. svn update from another branch just leaves the wc changes in place.

11:12 AWizzArd: In git I found it a bit cumbersome to always keep in mind in what branch I was.

11:12 rhickey: cemerick: but I'm not working in place like I would be with git

11:13 with svn I used separate dirs

11:13 cemerick: oh, you were referring to hg

11:13 noidi: the best part about in-repo branches is that everything stays in its own path

11:13 cemerick: irc strikes again :-)

11:13 noidi: in the same path

11:14 for example, with hg you would have to constantly change the classpath to point to whatever branch you're working on

11:14 rhickey: cemerick: no, git, switching branches in place with uncommitted changes on disk

11:15 Chouser: if you try to switch branches with uncommitted changes, it will refuse to switch

11:15 rhickey: Chouser: ah, thanks

11:15 Chouser: error: You have local changes to 'foo.clj'; cannot switch branches.

11:15 rhickey: so, it seems like git's in-repo branches might really matter to me...

11:16 and if you have 'shelved' stuff and push to server, they just get left locally?

11:16 (git here)

11:16 cemerick: yes, which is why I kept on bringing up temp branches.

11:16 AWizzArd: btw rhickey, you maybe want to read check out this comparison: http://rg03.wordpress.com/2009/04/07/mercurial-vs-git/

11:17 rhickey: cemerick: yes, I guess then that shelving wouldn't be as much of a feature for me then

11:17 Chouser: I use stash specifically to transport changes to a different branch, or to set aside changes for a minute while rebasing.

11:18 I don't use it for any longer-term storage than that, though I suppose some people might.

11:18 cemerick: it would still be useful for super-quick stuff (i.e. "damn, this should be on a different branch"). But yes, you wouldn't get as much mileage out of it as others would.

11:18 I have stuff in a stash for weeks, sometimes. I guess I should create a temp branch for that kind of stuff, but I generally don't.

11:19 noidi: the four killer features of git (which made me convert from hg) were svn support, history rewriting, stashing, and local branches

11:19 Chouser: this is one of the benefits of command line tools that spew text. you're a 3-line bash script away from "git-temp-push"

11:19 noidi: the three latter, when used together, allow you to develop in any way you want and forget about version control, but still end up with a nice history of changes

11:19 cemerick: noidi: +1

11:20 noidi: this blog summarizes it well http://tomayko.com/writings/the-thing-about-git

11:20 especially "Git means never having to say, you should have"

11:23 rhickey: It also seems like, when looking at hosts, multiple repos for a single project is less necessary with git, given in-repo branches, whereas each branch would be a separate repo on the server in hg?

11:24 Chouser: the bulk of my git experience is when used with an svn server :-) ...so I can't speak to that.

11:26 cemerick: AFAIK, there should be a maximum of one repo per user server-side (assuming each user forks the repos they have access to), regardless of the SCM. The difference with branches only means that there's one working copy per branch you check out in hg, but generally only one working copy per repo with git.

11:26 AWizzArd: rhickey: you don't need different repositories/directories. Those changes would just not be merged until you are ready.

11:26 rhickey: when I evaluated hg use for commercial dev, it was a big concern that work in progress not live only on dev's local drives, so that meant mirroring their local repos on a server anyway

11:27 AWizzArd: In the visualization for hg you would see how one or two lines spread away from the history (the new branches).

11:27 rhickey: http://www.infoq.com/resource/articles/dvcs-guide/en/resources/tortoise-hg.png

11:28 in this upper window you see that yellow and green line

11:28 rhickey: cemerick: I'm not clear on that - say I'm working by myself on 3 branches at once, with git that's one server-side repo, with hg?

11:28 AWizzArd: It seems that hg works well for the Firefox and NetBeans developers.

11:29 drewr: rhickey: There's a social issue in there somewhere. git's philosophy is that since branching, committing, etc. is so cheap, that you should do it frequently. There really shouldn't be longstanding works in progress that aren't commits.

11:29 AWizzArd: rhickey: you can have it all in one repo in hg. It just depends on how deep the changes are. Typically one is not doing huge branches in one single repo.

11:29 rhickey: drewr: still, commits to branches

11:30 AWizzArd: rightm, that's my point, if I need to work on a feature that will take weeks, it will need its own branch and repo on the server in hg?

11:30 but its own branch in-repo in git

11:31 is that right?

11:31 drewr: You can do in-repo branches (they're called named branches) in hg, but they're painful.

11:31 scottj: I have a function that calls pmap and if I call it from the repl it returns immediately after printing its output, but if I run the same program without the repl (as a script), it prints the output but waits one minute to quit. If I change the pmap to map it quits immediately after printing the output (total time roughly 5 seconds). pmap is being applied to a coll of 11 items, so 11 threads I think. It shouldn't take that long to close them should it?

11:32 Chouser: scottj: you need to shut down the agent pool

11:32 drewr: Chouser: Just doing that doesn't help.

11:32 I've been having the same problem.

11:32 Chouser: oh?

11:32 cemerick: rhickey: again, AFAIK (which isn't a ton w.r.t. hg deployment issues -- just what I've gleaned from friends that use it), there's still just one repo server-side with hg

11:33 scottj's problem came up on the mailing list, too.

11:33 drewr: Chouser: http://groups.google.com/group/clojure/msg/7490698d3e4a6ed1

11:33 AWizzArd: It could be in one repo. I just think this is not typical for hg users.

11:33 cemerick: it's a problem with c.l.Agent using a default thread pool that uses non-daemon threads.

11:33 naeu: what's the motivation for having both vectors and lists in clojure?

11:33 Chouser: sorry, I'm hardly reading the list at all anymore. :-/

11:33 rhickey: cemerick: not one for stable, one for dev etc? that's what I see on bitbucket

11:34 cemerick: rhickey: oh, you *can* do that, but I don't think it's required. Maybe that's idiomatic? (Maybe I'm simply wrong :-) )

11:34 AWizzArd: Here are some screenshots showing how someone merged different branches inside one hg repo: http://bitbucket.org/tortoisehg/stable/wiki/screenshots

11:35 drewr: Chouser: I don't blame you. I can skim it at best.

11:35 rhickey: cemerick: if daemon, then people would complain they didn't finish

11:36 AWizzArd: And btw, http://rg03.wordpress.com/2009/04/07/mercurial-vs-git/ is a fair comparison.

11:36 drewr: scottj: What's worse is that a script invoked from cron seems to never exit.

11:36 I have no idea why that execution environment should have different behavior.

11:37 noidi: is it possible to define some code in a Clojure module that's only executed when the file is run using clojure.main?

11:37 cemerick: rhickey: why would that happen? All the 'daemon' indicates is that the threads won't prevent the exit of the vm.

11:37 scottj: drewr: Chouser: luckily for my script adding shutdown-agents did cause it to quit immediately. w/o shutdown-agents: 1m6s, w/ shutdown-agents: 3s

11:38 rhickey: cemerick: right now they can fire off work in agents, call shutdown-agents, and it will wait for those jobs already running to complete, if daemon they'll die immediately

11:40 * rhickey adds explicit agent thread pools to todo

11:42 rhickey: thanks all for the dvcs info - moving to a different machine now...

11:44 cemerick: rhickey: I guess I'd say that perhaps shutdown-agents should include a call to awaitTermination, assuming shutdown-agents is only usefully called in -main

11:45 rhickey: cemerick: ok, patch welcome

11:46 cemerick: heh. :-P The problem being, of course, that I've never actually used agents.

11:47 rhickey: ok, issue welcome then :)

11:47 danlarkin: jdk7 gets invokedynamic yay

11:47 cemerick: yeah, I could do that :-)

11:47 rhickey: with your suggestion for patch

11:47 bbl

11:47 cemerick: maybe drewr is a contributor?

11:48 * drewr is indeed

11:48 drewr: I can try it out.

11:49 noidi: can I somehow check if I'm in the "user" namespace?

11:49 cemerick: noidi: there's probably some clojure.core var that indicates what filename(s) were provided to clojure.main

11:49 danlarkin: ,ns

11:49 clojurebot: java.lang.Exception: Can't take value of a macro: #'clojure.core/ns

11:49 danlarkin: ,*ns*

11:49 clojurebot: #<Namespace sandbox>

11:49 noidi: I'm writing some unit testing code, and would like each test module to be runnable from the console

11:49 but also to be able to run all the tests at once

11:49 drewr: (= (str *ns*) "user")

11:49 ,(= (str *ns*) "user")

11:49 clojurebot: false

11:49 drewr: ,(= (str *ns*) "sandbox")

11:49 clojurebot: true

11:50 magnet: <rhickey> cemerick: still, the need for that, and lack of support out of the box is a bad smell to the ideology of git, IMO. I hate C programs, shell crap etc. Tools like this should be written in portable HLLs like Python/Java, have good HTTP interfaces etc.

11:50 there's JGit which is Git ported in Java

11:50 and afaik it's fully compatible, up-to-date and people are maintaining it

11:50 (sorry to bump in so late :)

11:50 cemerick: drewr: the patch would be pretty easy. I think. :-)

11:51 I just have no way to test/confirm it.

11:51 noidi: i dislike git, both its interface and the implementation, but it's the only tool that lets me do whatever I want, so that's what I use :)

11:51 magnet: well with JGit I think more graphical tools/sugar will come and make it easier to use for SVN people

11:52 noidi: I hope so

11:52 magnet: in fact Eclipse is considering moving to Git and they are planning on putting a lot of support to their Git plugin (EGit, based on JGit, by the same developer)

11:52 drewr: cemerick: I don't have a reliable way to reproduce it either.

11:52 magnet: and that dev is Shawn Pearce (Google) who did Gerrit, a web interface for tasks that works over git

11:53 drewr: It doesn't happen with my isolated tests.

11:53 cemerick: drewr: I thought you had some code that was falling down?

11:53 ah.

11:53 AWizzArd: Just recently the Python devs decided to use hg.

11:53 kotarak: Surprise....

11:53 AWizzArd: Also NetBeans sits in a hg repo.

11:53 kotarak: Mozilla does also... And OpenJDK, OpenSolaris, ....

11:54 Xen, AFAIK.

11:54 cemerick: 4 out of 5 dentists agree.... :-P

11:54 AWizzArd: I think both candidates offer basically all Rich needs.

11:55 kotarak: cemerick: sorry. I have to cheer for hg a bit. ;) git has enough support out there.

11:55 * rsynnott wonders why VCSs must be such a religious issue

11:55 danlarkin: this really is vi vs. emacs

11:56 * kotarak personnally thinks, the neither hg nor git buy each other a lot. rhickey should use what he's comfortable with.

11:56 cemerick: kotarak: no need to apologize. I want vigorous competition.

11:56 magnet: Using git: Linux, Android, most of Freedesktop.org, OLPC, Compiz Fusion, most Ruby projects, some Debian stuff, Fedora, GCC, Qt (moving to it), ...

11:56 cemerick: indeed, one's choice is completely dependent upon specific circumstances and taste.

11:56 magnet: cemerick, I agree :)

11:57 AWizzArd: magnet: I think when we add their LOC they come close to what the OpenJDK uses alone ;-)

11:57 Anyway, gtg

11:57 cemerick: appeals to who's using what are mostly useless though, assuming critical mass achieved on both sides.

11:58 magnet: yeah, but there are two kind of projects; stuff for end-users and stuff for developers (libraries, languages, ..)

11:58 Chouser: actually, it is a bit worse than emacs/vi. rhickey's choice of editor has little effect on me, while his choice of vcs has a direct impact

11:58 magnet: maybe hg has more dev-tools projects

11:58 drewr: cemerick: Is this awaitTermination() what you were thinking? http://is.gd/lBeB

11:58 cemerick: Chouser: indeed. We seriously need a git-hg, just to cover our asses. :-D

11:59 kotarak: I would prefer hg, because 1) it's not written in C (only a small perf critical part), 2) has a nicer UI and 3) my encounters with the community where nicer than that with the git guys. Oh. And 4) I don't have to GC my repo. So as you say, cemerick, personal preference and taste mostly...

11:59 cemerick: drewr: right -- adding a call to that for each executor in c.l.Agent, and setting up both executors to use ThreadPoolFactories that emit daemon threads should do the trick.

12:00 drewr: Whoops, I meant http://is.gd/k43a

12:00 danlarkin: Chouser: you're right... at least vi & emacs can both edit the same files. Would be nice if git/hg could interoperate

12:01 naeu: I think the biggest benefit of git currently is the wonders of github, which really gives an amazing community experience for working together on a shared codebase - bitbucket pales in comparison

12:01 kotarak: danlarkin: the that's an interesting idea: Why isn't there a common protocol for exchanging changesets? So hg can pull from git, git can push to monotone... That would be nice. But I figure the information associated with the changes are too different between the systems...

12:02 magnet: kotarak, well, there's diff ;)

12:03 gnuvince: cemerick: I don't know about git-hg, but hg-git is a Google summer of code project.

12:05 cemerick: another "surprise, surprise" :-)

12:15 hiredman: I am using zip-filter and tagsoup to screenscrape a webpage, is there some way to filter on a certain css class with zip-filter?

12:28 Chouser: hiredman: not built in, but you coulre write a fn like attr= to do it for you.

12:28 could

12:34 hiredman: hmm

12:46 http://weblogs.java.net/blog/forax/archive/2009/04/invokedynamic_n_1.html

12:46 noidi: macroexpand-1 doesn't seem to work in tests defined using test-is's deftest

12:47 !paste

12:47 cgrand: hiredman: you can use some parts of Enlive to screenscrap: (select (html-resource "http://clojure.org/&quot;) [:.my-class]) return a seq of nodes

12:47 noidi: ,paste

12:47 clojurebot: java.lang.Exception: Unable to resolve symbol: paste in this context

12:48 noidi: clojurebot, what's the url to paste code snippets?-)

12:48 kotarak: lisppaste8: url

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

12:48 danlarkin: hiredman: *cheer*

12:49 hiredman: I just ended up doing (-> soup first :content :first ...)

12:51 lisppaste8: noid pasted "deftest and macroexpand-1" at http://paste.lisp.org/display/79305

12:52 noidi: is there any way around that?

12:52 I'd like to write unit tests for my macros

12:54 AWizzArd: You can do that

12:54 noidi: hmm, one way seems to be to capture the macro expansion in a let form around the deftest

12:54 AWizzArd: In general the idea is to compare the expansion of the macro

12:55 ,(macroexpand-1 '(when true 15))

12:55 noidi: AWizzArd, that's what I'm trying to do, but macroexpand-1 doesn't work inside the deftest

12:55 clojurebot: (if true (do 15))

12:55 AWizzArd: it doesn't work in deftest? That sounds strange

12:56 noidi: see my paste

12:56 it's as if the macro wasn't defined at all

12:56 oh well, wrapping the deftest in a let works well enough as a workaround for now

12:56 hiredman: hmmm

12:57 cgrand: noidi: in which ns do you run your tests?

12:57 lisppaste8: noid pasted "deftest and macroexpand-1 (workaround)" at http://paste.lisp.org/display/79306

12:57 AWizzArd: noidi: for me it works

12:58 noidi: here's my (non-anonymized) workaround

12:58 Chouser: cgrand: I still haven't taken a look at enlive, though it sounds great. Did you end up using zip-filter inside?

12:58 AWizzArd: noidi: If I (run-tests 'my.namespace) or if I call your (test-foo) directly. bar gets printed.

12:59 noidi: cgrand, that's a good point, they get run in user

12:59 cgrand: Chouser: I used zip-filter at some point but not any more

12:59 noidi: cgrand, thanks

12:59 cgrand: and user/foo doesn't exist

12:59 rhickey: any reason not to remove the assert-if-lazy-seq stuff now?

13:00 noidi: it'd make sense if the tests would be run in the namespace that they're defined in

13:00 okay, that sentence itself did not make any sense, though :)

13:00 Chouser: cgrand: was it's inability to change the tree along the way a factor in your abandoning it? Or something else?

13:01 noidi: i'll try again: I think it would be logical that each test would be run in the context of the namespace that it was defined in

13:01 AWizzArd: noidi: how do you run the tests?

13:01 Chouser: I suspect enlive is better than zip-filter for all of zip-filter's use cases...

13:01 cgrand: Chouser: with zip-filter the hard part was to sort/merge the locs returned by the filter

13:01 Chouser: cgrand: are you using zippers at all?

13:02 noidi: AWizzArd, I call a .clj from a shell using clojure.main

13:02 Chouser: rhickey: no objection here -- I've been forgetting to build clojure with the flag for weeks now.

13:02 cgrand: Chouser: I'm exclusively using zippers

13:15 Chouser: whoa

13:16 rhickey: whoa what?

13:16 Chouser: big commit

13:16 :-)

13:16 rhickey: not realy

13:16 really

13:17 Chouser: oh

13:21 Lau_of_DK: Good evening guys

13:28 cgrand: evening sir

13:34 rhickey: what's the problem with closed-over clearing in lazy-seqs and delays? Is it related to streams?

13:36 technomancy: the docstring for *file* says it's nil in the REPL, but it actually returns "NO_SOURCE_PATH"

13:37 rhickey: cgrand: I haven't changed the behavior, only the promise. I'd like to potentially support source-code backends, where doing that might be prohibitive, but once streams are in play there are other ways to do similar things. Basically, I don't want to be tied to the current behavior

13:37 technomancy: should that be an issue+patch?

13:38 cgrand: rhickey: ah ok, thanks

13:46 technomancy: nice; looks like invokeddynamic made it into openJDK.

13:55 hrm; looks like a recent test-is change broke clojure-test-mode

14:24 rhickey: anyone using/tried Assembla?

14:58 cemerick: it seems like conj should use a bindable var to determine what to produce if the provided coll is nil. e.g. (binding [*nil-conj* hash-set] (conj nil 5)) => #{5}

15:01 hiredman: :(

15:01 arohner: cemerick: why a var? why not another argument?

15:02 cemerick: arohner: I think that would make things difficult if not impossible given the contracts of alter, update-in, etc.

15:04 hiredman: why :-( ?

15:05 rhickey: cemerick: why not a builder fn (alter x (conj-on hash-set) foo)?

15:06 hiredman: cemerick: nil is not part of the set of hashset things

15:06 if that makes sense

15:07 operations that produce hash-sets should not also produce nil?

15:08 nil is (or was?) part of the contract for sequences, so nil stands in as the empty seq in a lot of places

15:09 rhickey: (seeded-conj #{}) ?

15:09 cemerick: rhickey: that certainly seems like a superior approach. I'm glad I don't censor my semi-stupid questions / thoughts.

15:09 hiredman: yeah, that makes sense

15:10 +1 for conj-on, or conj-with perhaps.

15:10 rhickey: I'm thinking the initial value is more succinct than the ctor fn, in general

15:10 hiredman: isn't that what into does anyway?

15:11 Chouser: #(apply conj (or % #{}) %&)

15:11 rhickey: hiredman: this function would return a fn like conj, but with special handling of nil coll

15:11 hiredman: I see

15:12 * technomancy takes note of %&

15:12 technomancy: did not know you could do that; very handy

15:12 cemerick: FWIW, what prompted this was my using update-in, but wanting to have sets created if no value was present at the key-path I provided. I ended up doing (update-in [keys] clojure.set/union #{new-value})

15:13 rhickey: cemerick: this comes up a lot of places

15:14 cemerick: yeah, I hit it all the time, usually wanting a set or vector instead of a list

15:14 rhickey: technomancy: %& my addition to the mathematica syntax I, um, borrowed

15:14 cemerick: I always wonder if named versions of the common cases would be useful, although conj-on or whatever covers all

15:15 technomancy: good artists copy, great artists steal.

15:15 =)

15:15 cemerick: rhickey: people *will* def conj-vec and conj-set using conj-on if it's not already def'ed in core

15:17 rhickey: ah, people

15:19 cp2: damn people

15:20 * rhickey wonders if JWebPane will escape Sun before being absorbed into [b]or[g]acle

15:22 technomancy: oh nice; it's based on webkit

15:23 I wonder if it could expose the DOM.

15:23 cemerick: rhickey: I've embraced Swing, once and for all. You'd be wise to do the same.

15:23 * cemerick cues ominous music

15:23 Chouser: every browser already exposes the DOM to applets, don't they?

15:23 cemerick: ha!

15:24 cemerick: Chouser: yeah, but that does nothing for desktop apps.

15:24 technomancy: Chouser: I've never looked at applets, but most "here's a browser" dynamic language bindings have been very poor in that regard.

15:24 rhickey: cemerick: I don't think I had the same use cases for it as you (full UI in HTML), but just embedding scenarios where it's really handy to have a solid web renderer

15:25 Chouser: cemerick: I assume you mean swing wrapped around webkit wrapped around canvas with a JavaScript interpreter implementing a widget toolkit that looks like swing?

15:25 cemerick: rhickey: long term, I think the former is a subset of the latter.

15:26 rhickey: http://java.dzone.com/news/future-jwebpane-and-evening

15:26 cemerick: Chouser: AFAIC, screw swing -- just webkit with some native-platform hooks and java interop will do me just fine

15:27 like, ahem, titanium, but with less suckage in key areas

15:27 Lau_of_DK: rhickey: Are you signed for JAOO DK yet ?

15:27 abrooks: Sun + Oracle --> S'nacle (rhymes with cynical...)

15:27 rhickey: Lau_of_DK: I'm not doing JAOO

15:28 Lau_of_DK: You must

15:28 rhickey: Alredy turned it down, sorry

15:28 already

15:28 Lau_of_DK: Im really sorry to hear that

15:28 You let your danish crowd down, all 5 of us

15:29 rhickey: Let's get everyone together in NYC for a Clojurefest sometime!

15:29 st3f4n: rhickey: i am just getting my feet wet with clojure and i very much like it. i'm just wondering .. do you think it will be possible in a future version to have much better error reporting? rihght now the java stack traces with the mangled names don't have a lot of info in them.

15:29 Lau_of_DK: Sorry - Im not getting trapped in your Police state! :)

15:30 st3f4n: rhickey: well, they do have a lot of info, but nothing really relevant pointing back to the actual clojure code :)

15:30 abrooks: rhickey: Hm. We're not quite ready for the Javits center... what venue did you have in mind for NYC?

15:30 Chouser: st3f4n: are you using slime/swank?

15:30 st3f4n: Chouser: yes i am

15:30 rhickey: st3f4n: you can make specific reports of confusing messages on the group, but most of the time people seem to be getting not good first-line messages from slime

15:30 Chouser: st3f4n: are you aware of how to get past the final cause?

15:31 st3f4n: Chouser: nope?

15:31 rhickey: vs the repl itself, which bubbles up the cause

15:31 Chouser: st3f4n: I think there's some kind of menu -- you can press 1 or something to see the next cause

15:31 technomancy: what ever happened to that clj-backtrace project?

15:31 Chouser: often the second or root cause gives you more helpful info than what slime prints by default

15:32 st3f4n: but wil clojure ever be able to say 'i don't understand this or that on line 123 of my-source.clj' ?

15:32 Chouser: yeah, it says that today

15:32 st3f4n: hmm

15:32 well i'll give it another tr

15:32 y

15:32 Chouser: well, not so much "i don't understand", but it names the .clj file name and line number almost all the time.

15:33 arohner: technomancy: I use clj-backtrace all the time

15:33 it's very nice

15:34 technomancy: arohner: it doesn't tie in with slime yet, does it?

15:34 arohner: not that I'm aware of

15:34 technomancy: just what I need; another backburner project. =)

15:34 arohner: I know the feeling :-)

15:35 replaca: Q: Is it possible to get arglist info on multimethods? It appears not.

15:36 you can do (methods m) but the resulting map has funcs with no metadata

15:37 * cemerick jots down another use-case for fn metadata ;-)

15:37 ciaran: anyone care to comment on my first bit of Clojure (Fibonacci)? http://pastie.textmate.org/private/90utvl5hrun7mrow8hvdcq

15:37 replaca: cemerick: I'm using it all over the place for doc generation and it's fabulous

15:38 cemerick: replaca: that's var metadata -- fns can't yet have metadata

15:39 Chouser: seems like you'd want arg metadata on the defmulti var or object as well, rather than any fn, wouldn't you?

15:39 cemerick: Chouser: all of the above -- args aren't necessarily the same among all methods

15:40 replaca: Chouser: hmm, well the arg data really lives on the individual methods. Does anything (other than convention) force it to be the same?

15:40 technomancy: ciaran: your defmulti dispatch function could be simpler

15:40 ciaran: #(= (#{1 2} %) %) could be {1 true 2 true}

15:40 since maps are functions

15:41 Chouser: all the methods for a multi better have at least roughly the same args, don't you think?

15:41 replaca: Chouser: Says who?

15:41 cemerick: decidedly not

15:41 Chouser: hm

15:41 replaca: Chouser: I mean, in most cases I agree with you, but there's nothing in the language that says so

15:41 ciaran: technomancy: and then use true / nil? Neat

15:42 Chousuke: couldn't it just be #{1 2}

15:42 replaca: Chouser: and therefore, the metadata should enable what the language enables

15:42 technomancy: Chousuke: yeah, but then you'd need a defmethod for one and a defmethod for 2, right?

15:42 Chousuke: though then you couldn't dispatch on false :P

15:42 cemerick: Chouser: consider a merge-interval multimethod, where you might have N different representations of intervals (series of numbers, pairs of vectors, Java-land impls, etc)

15:42 technomancy: unless there's a way to have a default defmethod

15:43 Chousuke: technomancy: hmm

15:43 cemerick: technomancy: that's what the :default dispatch value is for

15:43 technomancy: cemerick: heh; yeah I just found that in the book.

15:43 Chousuke: technomancy: well you could #(boolean (#{1 2} %))

15:44 replaca: Chouser: One thing I see people do is add different tag data for the different method sigs

15:44 Chouser: cemerick: but aren't those args representing the same thing? The intervals to be merged?

15:44 technomancy: ciaran: http://p.hagelb.org/fib.clj.html

15:45 replaca: none of which answers my original question! :-)

15:45 cemerick: Chouser: sure, but the 'unpacked' version makes it very different. (merge-intervals [1 5] [4 7]) vs (merge-intervals 1 5 4 7)

15:46 ciaran: technomancy: cool

15:47 Chouser: cemerick: so what are the two different arg metadatas you'd like to see?

15:48 replaca: Chouser: aren't I the one who wants arg metadata?

15:48 cemerick: I was going to say :-) But at the very least, an indication of arity.

15:50 Chousuke: I think a merge-intervals function that takes a variadic number of arguments and implicitly assumes they're start/end pairs would be silly.

15:50 replaca: right, what I imagine is being able to get three things: (1) The discriminator function, (2) the various discriminator values, and (3) the arglists for each func. #2 I can get currently, the others are inaccessible

15:51 I would, for example like to be able to produce better doc here, for instance: http://code.google.com/p/clojure-contrib/wiki/DuckStreamsApiDoc#reader

15:52 On Stuart's fabulous reader multimethod

15:53 I could just read the source, but that breaks down pretty quickly when folks use macros and such

15:53 and feels very hacky

15:54 cemerick: Chousuke: it was just the first thing I thought of, although we do have a bunch of fns that interpret sets of four numbers as defining rectangles. Doing so makes all of your operations a simple reduce instead of having to cons up tons of intermediate container objs.

15:57 stuhood: ~def struct

15:58 Chousuke: cemerick: I guess it's useful sometimes. but in this case, since splitting the stream of numbers into pairs just requires a partition call, I'd leave that up to the caller rather than have some special code for plain integers. It just feels fragile :)

15:59 stuhood: ~def c.l.PersistentStructMap

15:59 Chouser: I guess arg metadata on a fn is of the sort that would never change and is thus easier to support.

15:59 same for the internal name of the fn

16:00 replaca: Chouser: ? I don't understand

16:00 Chouser: easier than what?

16:00 Chouser: easier than allowing metadata on fns to change after the fn is defined.

16:01 replaca: Chouser: and how does that relate to multimethods?

16:02 cemerick: stuhood: were those for me? :-) We use PSMs extensively (see my genbean posts from months ago, though our current impl is quite a bit cleaner/advanced since the last time I posted on it).

16:03 Chouser: when you do a defmethod, you provide the function definition along with the arg lists, so they could all be created at once (the easy-to-support case)

16:03 stuhood: cemerick: oh no, sorry. was just wondering how struct was implemented, and now i'm wondering why it doesn't use a class with final members

16:04 cemerick: stuhood: I believe that's where rhickey is planning on going, although it's likely a low-priority item.

16:04 Chouser: contrast that with, say, a docstring on each defmethod, where you could be forgiven for wanting to update the docstring at runtime (hard to support)

16:04 stuhood: cemerick: gotcha: thanks.

16:04 replaca: Chouser: yeah, but the defmthos is aware of the defmulti, so it would be pretty easy to inherit an arglist. Is that what you mean?

16:04 cemerick: I have the impression that it's sort-of low-hanging fruit if one understands the asm stuff well enough (which I don't)

16:05 replaca: *defmethod

16:05 Chouser: replaca: hm, no. I feel like we're talking completely past each other. :-/

16:06 replaca: Chouser: that's what I thought. You're just talking about :doc?

16:06 Chouser: replaca: you're wanting each defmulti to add its arglists and dispatch value to the defmulti's metadata or something?

16:07 replaca: Chouser: I don't care where it puts it, I just want it somewhere!

16:08 Chouser: the disatch value is alread there (it's the key in the result to (methods)

16:08 *dispatch

16:10 Since funcs don't support metadata now, an interesting thing might be to add it as a map on the methods function itself

16:11 but I don't know how that would mix in the internals

16:45 st3f4n: does clojure have a sorted set?

16:45 stuhood: st3f4n: yes: (sorted-set)

16:45 st3f4n: well, that was obvious :)

16:46 stuhood: ,(doc sorted-set)

16:46 clojurebot: "([& keys]); Returns a new sorted set with supplied keys."

16:46 scottj: Is there a function to combine two regexes? str and then re-pattern doesn't work well because somethings in regexes aren't allowed in strings (e.g. \^)

16:47 oh duh, "\\^". sorry

16:47 st3f4n: is there a way to specify the compare function for the sorted-set ?

16:48 arohner: is anyone aware of a good tool to remove unneeded .class files from a jar? I've successfully created on jar out of a ton of jars, but the file is 70mb and I'm pretty sure I'm not using many of the classes

16:48 stuhood: scottj: yea, or you could use clojure's #"" literal regex syntax (which just makes a java.util.regex.Patten), and combine the .toString of each pattern

16:48 rhickey: st3f4n: http://code.google.com/p/clojure/issues/detail?id=76

16:49 st3f4n: ah!

16:49 stuhood: ,(str (.pattern #"first") (.pattern #"second"))

16:49 clojurebot: "firstsecond"

16:50 stuhood: ,(java.util.regex.Pattern. (str (.pattern #"first") (.pattern #"second")))

16:50 clojurebot: java.lang.IllegalArgumentException: No matching ctor found for class java.util.regex.Pattern

16:50 hiredman: arohner: proguard maybe? it gets mentioned a lot in #java but I am not sure what it does

16:50 stuhood: ,(java.util.regex.Pattern/compile (str (.pattern #"first") (.pattern #"second")))

16:50 clojurebot: #"firstsecond"

16:51 arohner: hiredman: yeah, I tried that. It dies with Java OOM when I give it all the memory the box has

16:51 hiredman: heh

16:51 that could be a problem

16:51 st3f4n: sorry if this is a dumb question, but according to good functional style programming, to add an item to an existing set I have to create a new set based on the old set plus the new item right? isn't that extremely inefficent when the set is somewhat large?

16:52 arohner: st3f4n: yes, but the clojure data structures are clever about re-using as much as possible from the old set

16:52 the overly simplified version: think of a linked list A->B->C

16:53 I can make a new list D->A->B->C

16:53 st3f4n: yes but the compare function is not attached to the set, so i assume it has to run every time i add a new item

16:53 instead of just finding the right spot to insert the new item

16:53 arohner: compare function? you mean to determine whether the item is allowed to go in the set?

16:54 st3f4n: sorted set

16:54 hiredman: I have not looked at the patch, but I assume as you add to the sorted set, it keeps the same comparator

16:54 Chouser: ,(re-pattern (str #"first" #"second"))

16:54 clojurebot: #"firstsecond"

16:54 hiredman: so you would not need to re-sort

16:54 arohner: you still have to look up the new value's value in the sorted (tree?)

16:54 so I expect that's O(log n)

16:55 and I don't think you can avoid that even in a mutable set

16:55 st3f4n: no that is fine

16:55 ah yes i'm reading the patch now

16:56 yeah i keep thinking about cons cells ala lisp, but this is different with a java backend

16:57 i guess i simply need to write some tests to figure out the timing

16:58 is this patch already in trunk or should i apply it?

17:01 oh it does not compile

17:01 hiredman: I imagine it is kind of stale

17:01 st3f4n: like old bread

17:19 cads: how do I print out a list of vars defined in a namespace, along with their doc strings?

17:19 st3f4n: hmm .. not sure what to do .. i need a sorted collection of max 5000 items, to which i frequently insert items and lookup ranges of items

17:19 cads: for example I want to search clojure.contrib.math for a function that does exponents and roots

17:20 kotarak: cads: (require 'clojure.contrib.math) (find-doc "exp")

17:20 Just a guess

17:21 Maybe (find-doc "clojure.contrib.math") works also. Not tested.

17:22 st3f4n: can (replace smap coll) also be used to remove items from a vector?

17:23 Chouser: cads: (doseq [[n v] (ns-publics 'clojure.set)] (println "==" n "==\n " (:doc ^v)))

17:24 st3f4n: no, vectors don't grow/shrink in the middle

17:24 st3f4n: hmm

17:25 Chouser: st3f4n: perhaps a sorted map and subseq?

17:26 st3f4n: i don't think sorted maps support nth

17:26 Chouser: no, but you could use Integers for the keys. that's not what you want?

17:27 st3f4n: what i'm trying to do is create a high score database .. so i need to keep max 5000 score/name pairs, sorted by score

17:27 cads: chouser, ns-publics is great! kotarak, that works too!

17:28 hiredman: how do I get a hold of a resource in a jar file? like a text file

17:28 Chouser: hiredman: in a particular jar file, or in any jar file in your classpath?

17:29 hiredman: the jar file the clojure file is inn

17:29 but any particular is fine

17:31 st3f4n: the set-sorted-by would work for me, but the patch is not official yet

17:32 Chouser: ,(.getResourceAsStream (clojure.lang.RT/baseLoader) "clojure/main.clj")

17:32 clojurebot: nil

17:32 Chouser: hm. That worked for me.

17:32 lpetit: Hi

17:33 hiredman: hmm

17:34 it works in the repl here

17:34 stuhood: sandboxing?

17:34 hiredman: must be

17:34 no IO

17:39 what is the flag that is set durring compilation?

17:40 *compile-files*

17:40 ?

17:40 kotarak: hiredman: yes (see gen-class.clj)

17:41 lpetit: hiredman: indicates whether the evaluation must also persist .class files during compilation

17:42 hiredman: sorry, misunderstood the question

17:43 hiredman: yay! my app has -v flag now :P

19:19 technomancy: oh nice! wondering if that was the last blocker for 1.0...

19:20 rhickey: technomancy: should be, although I expect it to have fallout of its own, given the new jar names

19:22 technomancy: cool

19:27 durka42: won't that break everybody's clj scripts

19:28 replaca: rhickey: are you still here?

19:28 rhickey: yes

19:29 replaca: rhickey: I was wondering if you saw the discussion on arglists doc for multimethods earlier

19:29 rhickey: replaca: yes

19:30 replaca: This is a problem (though maybe not an urgent one) for the contrib auto-doc robot

19:30 rhickey: did you have any thoughts on a good approach?

19:32 rhickey: what's wrong with (defmulti #^{:arglists '[() (foo)]} bar type)

19:33 user=> (:arglists ^#'bar)

19:33 [() (foo)]

19:33 user=> (:arglists ^#'rest)

19:33 ([coll])

19:33 replaca: Ahh, so implementers should mark the defmulti themselves?

19:34 (or we could add a macro that added it, I suppose)

19:34 rhickey: replaca: they sort of have to, no? I understand some sugar in defmulti would be nice, but not until 1.1

19:34 replaca: Yeah, that's a reasonable approach.

19:34 rhickey: I think docs and arglists should go on the multi, not the methods

19:35 replaca: rhickey: Yeah, I agree with that, but if they're there, they should probably be enforced

19:37 for the moment, I'll just have the auto-doc say, "no argument data provided" or something if the contributer hasn't put anything on the defmulti

19:37 rhickey: thanks

19:37 rhickey: replaca: one problem with enforcement is that you could handle 4 args with [a b c d] or [a & bcd]

19:38 so the thing on the multi is a minimal spec

19:38 not a hard rule

19:39 replaca: rhickey: yeah, I would say that enforcement meant that the sigs matched. (Or better yet, if defmulti had a sig, defmethod didn't)

19:39 rhickey: you know, DRY

19:39 rhickey: sounds liek trouble to me :-)

19:40 rhickey: methods shouldn't have sigs

19:41 replaca: rhickey: OK, now I'm with you. That seems like a big breaking change, though

19:41 technomancy: so fns can't have metadata attached to them; right?

19:41 I'm working on creating a chain of fns and would like to note which are io-bound (for send-off) vs cpu-bound... wondering what the best way to work around the fn metadata problem would be.

19:42 replaca: technomancy: experimentation shows that to be true :-)

19:42 dnolen: technomancy: proxying Afn is a good workaround

19:42 technomancy: dnolen: what's the A stand for?

19:43 dnolen: AFn the class sorry

19:43 replaca: dnolen: can you put metadata on the proxy then?

19:43 dnolen: yes

19:43 replaca: dnolen: cool, I nver thought of that

19:43 technomancy: dnolen: yeah, I know; what's it mean?

19:44 how's it different from Fn?

19:45 lisppaste8: dnolen pasted "proxying AFn" at http://paste.lisp.org/display/79319

19:45 dnolen: here's an example from my own code

19:46 I take a function, proxy AFn, add metadata and when invoke is called on the proxy, the fn is called.

19:47 technomancy: seems a bit heavy-handed, but it gets the job done I guess.

19:48 stuhood: technomancy: the A stands for abstract

19:48 dnolen: yeah, metadata on fns is coming one day, but this is OK if you really really need it, in my case I absolutely did.

19:48 technomancy: dnolen: thanks. I suspect it'd be cleaner to do it without metadata in my case.

19:50 * technomancy votes for the issue on google code

19:51 dnolen: might already be there.

19:52 technomancy: yeah, it is. google code lets you "star" existing issues as a way of saying you care about it

19:52 dnolen: I think rich has this in a decent place on his internal priority list, he's the one that pointed me to the temporary solution :)

19:52 technomancy: speaking of google code, if anyone could review my symbol/keyword name verification patch, I'd be much obliged: http://code.google.com/p/clojure/issues/detail?id=13

19:53 dnolen: good to know. =)

20:00 bradford: question about try-finally form

20:00 (defn with-hbase [table-name column-families f]

20:00 (try (create-hbase-table table-name column-families)

20:00 (def result (f))

20:00 (finally (disable-table table-name) (drop-hbase-table table-name) result)))

20:01 If I want to get the result of evaluating the function while the resource (hbase) is up, and then return the result of evaluating the function from this function...

20:02 then what is the best way for me to capture the result the comes from evaluating the function f within the try

20:02 replaca: bradford: so what's the "def" about there?

20:02 bradford: well its not valid of course :-) ... iw as jsut playing with different let bindings and such to try to get the resilt in scope

20:03 replaca: bradford: I believe that the try should return the last result before the finally

20:03 ,(doc try)

20:03 clojurebot: java.lang.Exception: Unable to resolve var: try in this context

20:03 replaca: oh, it's a special form

20:04 ,(try 1 (finally 2))

20:04 clojurebot: replaca: I don't understand.

20:04 replaca: huh, works in my repl

20:05 so that form there returns 1 as it's result

20:05 technomancy: bradford: the finally clause doesn't affect the return value

20:05 replaca: despite the fact clojurebot doesn't know it

20:05 bradford: ,(try 2 (finally 3))

20:05 clojurebot: bradford: Gabh mo leithsc�al?

20:05 bradford: yes, it works in my repl too

20:05 replaca: the ways of clojurebot are known only to hiredman

20:06 bradford: does that answer your Q?

20:06 technomancy: replaca: and presumably to those who hired him

20:06 bradford: ah, ok, i found my issue

20:06 replaca: technomancy: :-)

20:08 bradford: ok, this works:

20:08 (defn with-hbase [table-name column-families f]

20:08 (try (create-hbase-table table-name column-families)

20:08 (f)

20:08 (finally (disable-table table-name) (drop-hbase-table table-name))))

20:08 and the client code for writings hbase tests looks liek this:

20:09 (deftest hbase-expr

20:09 (with-hbase "biz" "bar:"

20:09 #(is (table-exists? "biz"))))

20:09 but you can see I have to delay the evaluation of the is form when passing it to the higher order with-hbase function...so is this telling me that with-hbase should be a macro rather than a function?

20:11 replaca: bradford: this might be a good place for a macro

20:11 hang on a sec and I'll point you at something

20:12 bradford: I initially made this a macro around the entire deftest and also had a weird implimentation that is now greatly cleaned up with teh try-finally form

20:12 (defmacro def-hbase-test [name table-name column-families test]

20:12 `(deftest ~name

20:12 (let [bloh# (create-hbase-table ~table-name ~column-families)

20:12 result# ~test

20:12 bleh# (disable-table ~table-name) blek# (drop-hbase-table ~table-name)] result#)))

20:13 but that was clearly not a good idea :-)

20:13 hiredman: ~google clojure tokyo

20:13 clojurebot: First, out of 1580 results is:

20:13 Caffeinated Simpleton � Blog Archive � Fifth: Static Storage and ...

20:13 http://justin.harmonize.fm/index.php/2009/03/fifth-static-storage-and-tokyo-cabinet/

20:14 replaca: bradford: but you could use that macros structure for tests, if you want

20:14 I do something similar here: http://code.google.com/p/clojure-contrib/source/browse/trunk/src/clojure/contrib/test_contrib/pprint/helper.clj#19

20:15 bradford: yes, the new macro will be much better...it jsut wraps that try-finally form in a with form - it doesn't need to wrapp the entire deftest in a macro...on the is form

20:15 replaca: or you could make the macro be inside the test and just have a "& body" for the body of the macro

20:22 gnuvince_: Good evening

20:23 bradford: replaca: thanks, i got it now

20:23 the macro solution looks nice

20:32 replaca: bradford: great!

20:51 st3f4n: is there a 'call this function n times and collect the results as a list' ?

20:53 Cark: your problem statement is a bit short

20:53 what would be the parameters of your function ?

20:54 for instance map would do that

20:54 st3f4n: like to build an list of n random numbers

20:55 dliebke: ,(doc repeatedly)

20:55 clojurebot: "([f]); Takes a function of no args, presumably with side effects, and returns an infinite lazy sequence of calls to it"

20:55 dnolen: a list comprehension would work as well

20:55 st3f4n: ah!

20:55 dnolen: ,(doc for)

20:55 clojurebot: "([seq-exprs body-expr]); List comprehension. Takes a vector of one or more binding-form/collection-expr pairs, each followed by zero or more modifiers, and yields a lazy sequence of evaluations of expr. Collections are iterated in a nested fashion, rightmost fastest, and nested coll-exprs can refer to bindings created in prior binding-forms. Supported modifiers are: :let [binding-form expr ...], :while test, :when test.

20:55 durka42: ,(take 5 (repeatedly rand))

20:55 clojurebot: (0.6485221618543977 0.401715904441031 0.3184120360134589 0.21260817161311518 0.5062688341938172)

20:55 st3f4n: yes this is good :-)

20:57 ciaran: is rand said to have side-effects?

20:58 Cark: yes

20:58 dliebke: yes, since the value it returns is different each time

20:58 hiredman: erm

20:58 I would say it isn't a function

20:58 dunno about saying it has side-effects

20:58 Cark: there is state behind the scene, the side effect is that this state is modified on each invocation

20:58 bradford: ,(for [x (range 10)] rand)

20:58 clojurebot: (#<core$rand__4553 clojure.core$rand__4553@1aaf84c> #<core$rand__4553 clojure.core$rand__4553@1aaf84c> #<core$rand__4553 clojure.core$rand__4553@1aaf84c> #<core$rand__4553 clojure.core$rand__4553@1aaf84c> #<core$rand__4553 clojure.core$rand__4553@1aaf84c> #<core$rand__4553 clojure.core$rand__4553@1aaf84c> #<core$rand__4553 clojure.core$rand__4553@1aaf84c> #<core$rand__4553 clojure.core$rand__4553@1aaf84c> #<core$rand__455

20:59 st3f4n: heh

20:59 Cark: ,(for [x (range 10)] (rand))

20:59 clojurebot: (0.9672556948667086 0.25158497043868533 0.33681956743322305 0.1738481107372174 0.4976804747050422 0.02733982062402951 0.6016067433768929 0.6258270580974905 0.09267327584099161 0.8219117044789643)

21:00 ciaran: Cark: but isn't that an implementation detail? And if there was no such state, but it still returned a different value each time (somehow), would it still be said to have side-effects?

21:00 hiredman: if you memoize a function, does it have side-effects?

21:00 Cark: it couldn't return a different value each time if there was no state

21:00 a pure function always returns the same result for the same parameters

21:01 ciaran: hm, would a function that returned the current time be said to have side-effects?

21:01 Cark: hiredman : memoization is a gray area ...how about lazy seqs ?

21:01 ciaran : yes

21:01 ciaran: as it returns a different value (potentially) but doesn't _alter_ any state

21:01 Cark: it returns the state of the world, which is always changing

21:02 ciaran: so a function is said to have side-effects if it relies on state, not if it changes some state

21:02 Cark: not really, it's more if it changes state as you said ...

21:02 ciaran: but a function which returns the current time does not change any state, it just reads it :/

21:03 Cark: for a time function we'd say this function is not pure

21:03 the end result is the same, there are many things you cannot do with such a function

21:03 you cannot cache it

21:04 ciaran: well I am really just wondering about the definition of the term side-effects, a function returning the time was just an example

21:04 * ciaran just reads http://en.wikipedia.org/wiki/Side_effect_(computer_science) :)

21:05 Cark: in the clojure doc, there are many places where it is said you shouldn't use a side-effecting function

21:05 in some places a time returning function would do, in some other it wouldn't

21:05 hiredman: I have used functions with side-effects in about all of them :P

21:06 Cark: booo

21:14 chessguy: howdy ya'all

21:16 st3f4n: oh nested #()s are not allowed

21:19 i'm allowed to do many things inside an (alter ...) right? the examples only do simple things, but i can pass it an anonymous fn and for example modify my list and then sort it

21:20 Cark: yes

21:26 st3f4n: hm cool

21:31 hm nasty .. (conj ..) on () vs []

21:33 hiredman: ,(doc conj)

21:33 clojurebot: "([coll x] [coll x & xs]); conj[oin]. Returns a new collection with the xs 'added'. (conj nil item) returns (item). The 'addition' may happen at different 'places' depending on the concrete type."

21:35 st3f4n: there is no such thing as defconst in clojure right?

21:37 cipher: ,(doc #())

21:37 clojurebot: java.lang.ClassCastException: clojure.lang.Cons cannot be cast to clojure.lang.Symbol

21:38 cipher: ,(doc ->)

21:38 clojurebot: "([x form] [x form & more]); Threads the expr through the forms. Inserts x as the second item in the first form, making a list of it if it is not a list already. If there are more forms, inserts the first form as the second item in second form, etc."

21:50 st3f4n: what is the clojure equivalent of this in java: for (String s : stringVector) { println(s); } ?

21:50 danlarkin_: (doseq [s string-vector] (println s))

21:51 st3f4n: ahhh doseq

21:51 yes :-)

22:06 ha. 925 requests per second for my 'high score json web service'

22:06 on an old imac

22:06 i like the jvm :-)

22:07 actually 1600 reqs/sec

22:07 danlarkin_: depends on what you're doing to produce that json...

22:11 arohner: if I want to get a resource out of a jar file of clojure code, what class do I use for .getResourceAsStream?

22:23 Chouser: (.getResourceAsStream (clojure.lang.RT/baseLoader) "clojure/main.clj")

22:28 st3f4n: well i'm pretty happy with my results for a couple of hours hacking code

22:31 gnuvince_: ,(let [v [1 2 3 4]] (time (dotimes [_ 1e7] (first v))))

22:31 clojurebot: "Elapsed time: 2117.945 msecs"

22:34 arohner: Chouser: thanks

22:34 chessguy: gnuvince what's _?

22:35 oh i see

22:36 gnuvince_: chessguy: just a convention to say "I won't use this binding"

22:42 chessguy: gotcha

22:42 yeah there's something similar in haskell

23:54 unlink1: Does a stdlib function like this exist? (defn domap [f coll] (doseq [x coll] (f x)))

23:54 A side-effective map which doesn't return its results or hang onto a reference to its head

23:55 s/side-effective/eager/

23:55 hiredman: ,(do dorun)

23:55 clojurebot: #<core$dorun__3993 clojure.core$dorun__3993@ef0a03>

23:55 hiredman: ,(doc dorun)

23:55 clojurebot: "([coll] [n coll]); When lazy sequences are produced via functions that have side effects, any effects other than those needed to produce the first element in the seq do not occur until the seq is consumed. dorun can be used to force any effects. Walks through the successive nexts of the seq, does not retain the head and returns nil."

23:56 unlink1: That's it except for the function application part.

23:57 hiredman: ,(dorun (map print (range 10)))

23:57 clojurebot: 0123456789

23:57 unlink1: So I guess domap = (dorun (map f coll))

23:58 How do I compose print with another function?

23:59 hiredman: same way you compose any function

23:59 doto is very handy for printing

23:59 ,(doc doto)

23:59 clojurebot: "([x & forms]); Evaluates x then calls all of the methods and functions with the value of x supplied at the from of the given arguments. The forms are evaluated in order. Returns x. (doto (new java.util.HashMap) (.put \"a\" 1) (.put \"b\" 2))"

23:59 hiredman: ,(doto 1 prn)

23:59 clojurebot: 1

23:59 1

Logging service provided by n01se.net