#clojure log - Nov 17 2009

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

0:00 jkkramer: i did add this to my emacs config a while back, maybe related? (define-key global-map (kbd "RET") 'newline-and-indent)

0:00 hiredman: declare just lets you defn functions that reference vars that are not defined yet

0:00 technomancy: jkkramer: the minibuffer has its own keymap; it shouldn't be affected

0:00 Draggor: hiredman: Is there any way to get around this? I greatly dislike the idea of having to procedurally define things

0:00 slyrus: technomancy: what was that we were saying about how awesome these magic download-and-install-package-manager-thingys are? :)

0:01 jkkramer: the only place i have swank-clojure installed is in ~/Library/clojure/swank-clojure

0:01 technomancy: slyrus: heh; yeah M-x clojure-install was always an interim solution until I could get elpa working.

0:01 hiredman: Draggor: I have no idea what "procedurally define things" means

0:03 arohner_: Draggor: the var must be declared before it can be referenced when compiling. At runtime, the var must have a value when it is referenced or you get a unbound var exception

0:03 if you don't want it to have a value, set it to nil

0:04 jkkramer: even with minor obstacles, clojure-mode made my life a lot easier. none of the other IDE/editor solutions i've tried were as good

0:05 although, now it's harder to poke fun at emacs users, since i am one now

0:05 arohner_: following your earlier example, something is calling (func1 some-arg) before it has a value. that can't work in any sane PL

0:07 slyrus: jkkramer: i'm so far gone that I have a hard time imagining that folks would consider using anything else (except climacs, of course... :) )

0:07 I assume most emacs users here use paredit?

0:08 jkkramer: i still hit ESC whenever i intend to invoke some command

0:10 technomancy: paredit is bliss.

0:10 jkkramer: i installed paredit but have no idea how to use it

0:11 technomancy: jkkramer: http://p.hagelb.org/paredit-outline

0:11 one day that will become a screencast... not sure when. =)

0:11 jkkramer: ooo

0:12 that's what i've been looking for. quick-start guide

0:12 you are awesome

0:12 lgas: technomancy: re. the clojure wide-finder from your blog, this version gets the time down to about ~2.7 seconds (from ~272) on the same input file I posted to your blog about: http://pastie.org/702158

0:12 it still doesn't really peg my CPUs during that 2.7 seconds though

0:13 technomancy: lgas: wow! so partitioning inside the pmap makes a pretty big difference?

0:14 lgas: yeah... otherwise I guess the unit of work that each thread is doing is too small.

0:15 I don't have time tonight but I'm going to try to pull down some larger logs and try processing them (and also changing the regex so that it matches more of our URLs).

0:15 technomancy: yeah, I realized the original regex was more complicated

0:15 also: it's hard to find sample log files!

0:18 lgas: Yeah. We have some decently sized log files, but I can't give them out unfortunately. The one I've been testing with is 1.1m lines. But out of that 1.1m lines it only produces 48 matching URLs and of those a couple occur 4 times, a couple 3, a couple 2, and most just once, so it may not be the best test.

0:23 Drakeson: is slime trunk still broken with clojure ?

0:24 (it broke for me around Oct 30)

0:24 technomancy: yep

0:24 slyrus: I'd put it differently. swank-clojure is broken wrt slime HEAD

0:24 the offending commit is: addc5be70a489cdc8ed555a2fed7273040da196e

0:24 before that you should be ok

0:24 technomancy: is it un-idiomatic to use doto in non-java contexts?

0:25 slyrus: (not to blame swank-clojure, as this clearly seems to be changes in slime that broke swank-clojure)

0:25 technomancy: Drakeson: if you must work from git, use the technomancy fork of slime on github; else use elpa

0:25 (I haven't fixed it, I just haven't updated it with the breaking change yet)

0:25 hiredman: technomancy: I think it must be

0:26 arohner_: technomancy: yeah, I'd like to see an example of that

0:26 slyrus: Drakeson: or, for a second opinion, just make sure you checkout the version right before addc5be70a489cdc8ed555a2fed7273040da196e :)

0:26 arohner_: I've never needed to outside of java-interop

0:26 technomancy: hiredman: I like (doto (str (:root project) "/Manifest.txt") (spit (str "Main-Class: " (:main project)))) much better than the let-using equivalent

0:27 but it reads a little funkily

0:27 hiredman: I see

0:27 technomancy: (apart from the fact that it's squashed onto one line)

0:28 arohner_: where is let-using?

0:29 qed: http://clojure.pastebin.com/m1c41118e -- could someone tell me why im getting an error that says recur is missing args?

0:29 hiredman: arohner_: it would be (let [x (str (:root project) "/Manifest.txt")] (spit x (str "Main-Class: " (:main project))) x)

0:29 technomancy: arohner_: left as an exercise to the reader =)

0:29 arohner_: oh, I thought there was an actual function named 'let-using'

0:30 hiredman: I have a marvelous example that this terminal is too narrow to contain

0:30 _ato: qed: you're calling recur with 2 args while your loop has 6 bindings

0:30 technomancy: hiredman: that trailing x bugs me

0:30 qed: _ato: hmm i thought i had this loop/recur stuff figured out

0:30 hiredman: technomancy: I as well

0:30 Drakeson: slyrus: I have been doing that ever since it broke

0:31 qed: _ato: do i have to do something to those args in recur?

0:31 could they just be empty lists?

0:31 Drakeson: technomancy: well, the rate of changes in slime trunk is fairly high. I would rather stick to a fork of swank-clojure

0:32 _ato: qed: they could be... then v2 v3 etc are going to be assigned to empty lists? is that what you want?

0:32 qed: no, i just want to run x2->x6 on every inc of cnt

0:32 Drakeson: but again, I haven't look into what actually broke between the two, so I don't know if slime is going in the wrong direction (to deserve a fork) or swank-clojure should just keep up with slime's changes.

0:33 technomancy: Drakeson: I don't mean "fork" in the negative sense, merely the "I've branched and haven't bothered to update" sense

0:34 Drakeson: oh, I made my "update-clojure" script to use a specific commit for slime since then

0:35 _ato: qed: ah, move all the v2 v3 v4 etc into a let-binding inside the loop then

0:36 Drakeson: btw, will it make sense to have swank-clojure be merged into upstream slime (as there is ruby, etc. backends for slime)

0:36 qed: _ato: before or after the loop's binding?

0:36 _ato: the bindings on loop should just be the ones you want recur to assign to

0:36 qed: is the let-binding part of the body of the loop

0:36 hiredman: http://gist.github.com/236676 <-- with-open? we don't need no stink'n with-open

0:37 _ato: qed: lets try this again: http://etherpad.com/XIbR7KzRQl

0:38 arohner_: hiredman: what about exceptions?

0:39 hiredman: arohner_: I'm not exceptional

0:51 * technomancy waits impatiently for github's top watched Clojure projects list to update

0:51 qed: :)

0:59 st3fan: i'm doing a clojure talk here in toronto on thursday

1:01 time for zzz

1:03 Drakeson: st3fan: where?

1:04 (assuming you still haven't zzz'ed!)

1:05 technomancy: folks: it's time to discuss what the collective nouns should be for Clojure terms

1:05 "An alignment of vectors"

1:05 "A lethargy of lazy-seqs"

1:05 "A cartography of maps" (sorry)

1:13 adityo: "An Agency of Agents"

1:14 technomancy: yes! that's what I'm talking about.

1:14 hiredman: (Secret) Agency

1:14 adityo: :)

1:14 _ato: A conspiracy of conses?

1:15 hiredman: (def #^{:private true} a [(agent nil (agent nil)])

1:17 technomancy: "𝅘𝅥𝅮 Secret ... Aaaaaagent Man 𝅘𝅥𝅯"

1:18 adityo: Look up the CAD, Clojure Agent Directory ;)

1:24 qed: Instead of 007 it's BigO1

4:21 ngoc: Hi, how to convert Java's ArrayList to Clojure's array and Java's HashMap to Clojure's map?

4:31 Chousuke: ngoc: (into [] arrayList) or (into {} hashmap) should work

5:22 djpowell: I suppose it ought to be possible to automatically generate a method-map from a datatype's fields. Would be quite handy so that I can have 'subclasses' that override the field accessors with more complicated logic, and then I don't need to change client code from (:my-field o) to (my-accessor o) if I decide that simple fields aren't good enough

6:05 AWizzArd: ~max people

6:05 clojurebot: max people is 214

6:05 AWizzArd: uh la la

6:43 krumholt: hi, is there a difference between sync and dosync?

6:44 rhickey: krumholt: dosync is built on sync

6:44 sync has a (currently unused) spot for options, dosync elides that

6:44 krumholt: rhickey, ok thanks

6:45 rhickey: prefer dosync for now

6:45 krumholt: will do thanks

7:21 djpowell: urgh, macros confuse me. I wanted to autogen a protocol based on a datatype's fields.

7:29 AWizzArd: and?

7:32 djpowell: i'm getting confused. as defprotocol is a macro, does that mean I need to write a macro to construct a call to it?

7:39 rhickey: djpowell: yes

7:40 but what are you going to generate for the fields - accessors? you already get them for free

7:42 djpowell: well, I want to (in some 'subclasses') override some accessors to do some more complicated stuff, like have fallbacks to other fields. so I don't want to have to change all client code to change from (:field o) to (accessor o)

7:42 so i wanted to have a base method-map that has accessors for the fields, that I can override

7:43 rhickey: djpowell: there is no equivalent to subclasses with deftype/protocols

7:45 djpowell: no, but I can have a bunch of deftypes representing some related entities, and have them implement a common protocol, along with a protocol for their type specific operations

7:46 and I can override the method-map from the defaults to type-specific versions where i need something more than getting the field directly

7:46 rhickey: djpowell: ok, I'm not trying to dissuade you, just don't want you to be disappointed later :)

7:47 so, right now the mechanics of defprotocol are wrapped in a macro. Exposing that as a function might be useful

7:49 djpowell: i'm just toying around with things at the moment, looking at how these features might help me. cause I have some java right now (as you can probably tell!), that really needs some of the dynamic features that deftypes/protocols seem to offer

7:51 AWizzArd: btw, rhickey, why is a hierarchy of types bad? You mention something like this on the Protocols wiki page.

7:56 rhickey: AWizzArd: the long answer would take hours. A short answer is, a) I didn't say that, and b) if you can provide features (protocols/mixins) without requiring derivation/hierarchy, that's better

7:57 lpetit: rhickey: what would you consider the most interesting "paper" on this subject, that anyone could read to make one's own opinion without making you repeat things over and over again ?

7:57 AWizzArd: Maybe in your future talks you will leak out more details about it :)

7:58 djpowell: rhickey: I might be ok with using keyword accessors, and distinct protocol methods for the cases where I need different behaviours. Not sure yet.

7:59 fogus_: lpetit: This one is not bad: http://www.scala-lang.org/docu/files/IC_TECH_REPORT_200433.pdf

8:00 rhickey: it's better because hierarchy creates a world in which some people are saying, "if something is an X, do this that way" while others may say "foo instanceof Y", and still others say "Y isa X" and managing the implications becomes impossible

8:00 fogus_: lpetit: Scala-centric of course

8:00 rhickey: fogus_: I don't think so. traits are a good example of what I'm trying to avoid with protocols

8:02 fogus_: rhickey: Interesting. I've always liked the discussion on the expression problem in that paper, but if you have a link more in line with your own thinking, then I would also be interested in reading it

8:05 rhickey: RE: Traits. You're trying to avoid them because they do not solve the problem that you presented above, no?

8:06 rhickey: I think Scala implicits shows they don't really address the expression problem in as shown in the paper

8:06 * cemerick tries to restrain himself from mocking scala's pretzel-like expressions.

8:06 rhickey: traits are classes, so can have impl they have no pure interface-like spec construct

8:06 protocols are specs only

8:07 trait introduce hierarchy and isa, protocols don't

8:07 you can't imbue a class with a trait other than with implicits, which requires an adapter allocation and introduces identity and other isa problems

8:08 vs extending a protocol to String

8:08 fogus_: rhickey: You had to mention implicits... they are the skeleton in Scala's closet. ;)

8:08 rhickey: and mixing in implementations by method map composition, no hierarchy required

8:09 so, yes, traits/implicits are in the same expression problem space, they are very different from protocols

8:10 protocols - pure specs, no adapters, compositional mixins

8:10 fogus_: I see that more clearly now.

8:11 rhickey: traits/implicits - impure specs, implicit adapters, hierarchical mixins

8:12 fogus_: rhickey: I have a lot of experience with Scala and it's M.O. so it's helpful for me to understand the comparison. Thanks

8:14 djpowell: hmm, juxt seems slightly handy for composing method impls from calls to other methods

8:15 perhaps

8:45 ohpauleez: when writing something like: (let [x [1 2 3 4 5]] (nth x (rand-int (count x)))) one could do: (let [x [1 2 3 4 5]] (nth x (-> x count rand-int))) ...

8:45 but is there something more concise?

8:48 hoeck: ohpauleez: I have written a little rand-nth function for this case, like (rand-nth [1 2 3 4 5]), couldn't find a more concise way

8:48 ordnungswidrig: hoeck: a rand-nth function is concise for the user of the rand-nth function

8:49 ohpauleez: is there a way to man-handle something like %1 when not making anon-functions/lambdas

8:50 for example: (-> x count rand-int (nth x %1))

8:50 which I guess isn't more concise, so nvm haha

8:57 st3fan: what does -> do?

8:58 chouser: ,(let [x '[a b c d e]] (->> x count rand-int (nth x)))

8:58 clojurebot: c

8:59 ohpauleez: it threads the values from one call to the next

8:59 chouser: st3fan: it rearranges your code

8:59 fogus_: st3fan: http://blog.fogus.me/2009/09/04/understanding-the-clojure-macro ;)

8:59 ohpauleez: thanks chouser!

9:00 st3fan: interesting :-)

9:02 ohpauleez: chouser: what's the rationale for quoting the vector

9:02 chouser: ohpauleez: so I didn't have to quote each of the symbols

9:03 ohpauleez: ahh right right

9:03 thanks

9:03 chouser: symbols instead of keywords or strings because I can type each with a single keystroke instead of 2 or 3. :-)

9:09 esj: ->> is related to -> how ? Doesn't seem to be on the reader section of the web-page.

9:09 ohpauleez: ,(doc ->>)

9:09 clojurebot: "([x form] [x form & more]); Threads the expr through the forms. Inserts x as the last 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 last item in second form, etc."

9:09 ohpauleez: it stacks on the last, not the second arg

9:14 esj: aah, brilliant, thanks.

9:16 ohpauleez: totally welcome

9:27 fogus_: Is it possible to destructure and coerce at the same time using `let`?

9:28 cemerick: "coerce"?

9:30 AWizzArd: ,(doc coerce)

9:30 clojurebot: Excuse me?

9:31 ordnungswidrig: watching rick's infoq presentation right now. I like the idea of identity beaing a series of values. Is there more information on this topic? I see that patch-theory is related to this,

9:31 cemerick: fogus_: do you mean hinting in a destructuring form?

9:31 if so, sure

9:31 fogus_: I'm ripping out the chars from a string and want to coerce them to int. At the moment I have (let [[a b] "xy"] (println (int a) ", " (int b)))

9:31 cemerick: sure, you can hint a and b

9:32 you can hint any symbols in a binding-form

9:32 regardless of destructuring, etc

9:32 chouser: hinting is not coersion

9:32 fogus_: cemerick: I don't think hinting gets me what I need.

9:32 chouser: fogus_: I think you have to use two separate steps

9:32 fogus_: chouser: As I suspected

9:33 chouser: this is not the only case where it feels like it would be useful to call more or less arbitrary functions during destructuring

9:33 I think languages with flexible pattern-matching mechanisms can allow that to some extent

9:33 cemerick: fogus_: oh, you are actually converting, sorry, didn't read the sample carefully enough

9:33 AWizzArd: fogus_: can you hae your let also be something like (map int "xy")?

9:33 hae ==> have

9:34 rhickey: chouser: do you have any examples in other langs?

9:34 fogus_: cemerick: no worries.

9:34 AWizzard: That's it!

9:34 AWizzArd: k

9:35 fogus_: ,(let [[a b] (map int "xy")] [a b])

9:35 clojurebot: [120 121]

9:35 fogus_: AWizzard: Thank you

9:36 chouser: rhickey: not off the top of my head, but I'm pretty sure I've seen somewhere a general mechanism where you can provide both a constructor and a deconstructor. I'll see if I can find it.

9:41 patrkris: hey, i'm looking for something to do for my masters thesis, and since I like Clojure very much, I thought about maybe doing something about distributed parallelism in Clojure? Along the lines of Erlang. What do you think?

9:42 ohpauleez: refs, STMs, agents, AND actors?

9:42 haha

9:42 patrkris: that's a stupid idea? :P

9:43 ohpauleez: no, I personally think it's good

9:43 I once wrote a networked namespace for python

9:43 to support a distributed testbed

9:43 patrkris: i don't know if it is even possible or whatever... I'd just like to work with Clojure ;)

9:43 fogus_: chouser: Would Scala's unapply mechanism fit your description? It's not an arbitrary function however.

9:44 rhickey: patrkris: I'd love to see more work done around predicate dispatch.

9:45 chouser: fogus_: I think that must be it. What I'm thinking of would have to be a feature of either Scala or Haskell, and it feels more like Scala.

9:45 but yes, I see that it is controlled by the type of thing being destructured, not arbitrarily specified at the moment of destructuring. :-/

9:46 patrkris: rhickey: have you or others done any work in that regard?

9:48 rhickey: I did some Chambers/Chen style stuff in a prototype for Clojure

9:49 but to move it forward I think you'd want some more sophisticated notions of logical implication, maybe combining it with the datalog stuff

9:50 the implications in Chambers/Chen are derived from the type hierarchy, or possibly hardwired numeric implications, not a general system

9:50 supporting arbitrary logic and good performance is still a research topic, AFAIK

9:52 ordnungswidrig: rhickey: do you think there a connection points between the idea of "identity being a series of values" and patch theory?

9:54 rhickey: patch theory as described where?

9:54 ordnungswidrig: rhickey: as implemented by darcs or git.

9:55 rhickey: well, git does not really patch theory but a repository is basically a collection of functions applied on a state in distributed manner. Like STM, I think.

9:55 rhickey: I know darcs has some theory behind it, GIT is definitely a persistent data structure on disk

9:55 but the whole notion of merging complicates things

9:56 ordnungswidrig: rhickey: STM has the same problem, or not? I two concurrent processes try to execute conflicting changes they'll have to be merged.

9:57 s/I two/If two/

9:57 AWizzArd: ordnungswidrig: I have this same issue with my in-ram db.

9:57 Clojure is fortunately already doing a lot of needed work for you.

9:57 ordnungswidrig: AWizzArd: but the merge problem exists anyway

9:57 AWizzArd: this is true

9:58 if you have a db and two threads snapshot it and calculate a new db each, then the first who wants to check is data in wins. The second thread will have to merge.

9:58 Each thread which makes a snapshot will have to create its own replay-log, from which merging is possible.

9:59 lpetit: AWizzArd: won't the commute function help here ?

9:59 ordnungswidrig: AWizzArd: using patch theory you can provide a way to merge the conflicting changes. e.g. inc and dec can be commuted without changing the result.

9:59 AWizzArd: lpetit: not really

9:59 ordnungswidrig: AWizzArd: what is the underlying data model of your db?

9:59 AWizzArd: ordnungswidrig: maybe we talk about different things

9:59 rhickey: ordnungswidrig: STM does no merging other than commute, otherwise there are retries

9:59 AWizzArd: ordnungswidrig: clojure data structures

10:00 rhickey: merging and operational state transform etc are all interesting

10:00 they get application-domain specific though

10:00 AWizzArd: right

10:01 rhickey: so, merging edits to a text file is an application domain

10:01 ordnungswidrig: rhickey: I expect the things to come together at some point of time. Yes, the domain specific part is often surprisingly easy.

10:01 rhickey: well, commute is there

10:01 you write the fn

10:02 ordnungswidrig: rhickey: merging text is hard because the intention of the change is lost. Talking of key value sture the merge model is easier.

10:02 Oh, there is a commute fn?

10:02 I mean a way to specify a domain specific one?

10:02 rhickey: Clojure's STM has commute

10:02 ordnungswidrig: rhickey: As I said. The things are coming together :-)

10:02 patrkris: rhickey: thanks - you mentioned a lot of things that I'll need to look up, but sounds interesting (distributed Clojure that is)

10:02 AWizzArd: commute allows for more parallelism than alter I think?

10:03 ,(doc commute)

10:03 clojurebot: "([ref fun & args]); Must be called in a transaction. Sets the in-transaction-value of ref to: (apply fun in-transaction-value-of-ref args) and returns the in-transaction-value of ref. At the commit point of the transaction, sets the value of ref to be: (apply fun most-recently-committed-value-of-ref args) Thus fun should be commutative, or, failing that, you must accept last-one-in-wins behavior. commute allows for more

10:03 fogus_: ,(doc commute)

10:03 ordnungswidrig: AWizzArd: a commute function can event be interactive. Like a mergetool in DVCS

10:03 clojurebot: "([ref fun & args]); Must be called in a transaction. Sets the in-transaction-value of ref to: (apply fun in-transaction-value-of-ref args) and returns the in-transaction-value of ref. At the commit point of the transaction, sets the value of ref to be: (apply fun most-recently-committed-value-of-ref args) Thus fun should be commutative, or, failing that, you must accept last-one-in-wins behavior. commute allows for more

10:03 fogus_: Whoops..

10:03 rhickey: patrkris: I wasn't suggesting work on distributed Clojure, but on (non-distributed) dispatch

10:04 ordnungswidrig: AWizzArd: is you db public?

10:04 AWizzArd: ordnungswidrig: i don't know at which level you want to be aware of changes for merges. I want highlevel merges. One entry in the replay-log resembles a full transaction.

10:04 ordnungswidrig: Not yet, I will be working on having it opensourced :)

10:04 Clojures snapshots are so cheap, I can have tons of them in ram.

10:05 patrkris: rhickey: see, I don't know anything... yet :)

10:05 AWizzArd: So, one can have undo, and other nice things.

10:05 chouser: fogus_: you were right. unapply.

10:06 ordnungswidrig: AWizzArd: seems like were working on similiar topics.

10:06 rhickey: patrkris: http://portal.acm.org/citation.cfm?id=320407

10:06 chouser: But it looks like you can do arbitrary work, you just have to stick it in an unapply method of a class.

10:06 AWizzArd: ordnungswidrig: gut :)

10:06 chouser: http://www.scala-lang.org/node/112

10:08 rhickey: chouser: do you get only one unapply per type?

10:09 fogus_: chouser: Arbitrary work yes, but still bound to the case class (or somewhere along its heirarchy)

10:10 lisppaste8: Chouser pasted "simplified unapply example" at http://paste.lisp.org/display/90579

10:10 patrkris: rhickey: thanks, i'll take a look. If you have any other ideas for a masters thesis subject, I'd be happy to hear them

10:10 rhickey: that is, additional ideas

10:11 chouser: So there's only one unapply for Twice, but because any number of classes could take an Int as their arg in unapply, and you could specify any of them in a case expression.

10:14 so just like [a] means roughly assoc in normal expressions but nth in destructuring forms, I suppose int could mean "convert a to an int" normally but "assign a to an int" in destructuring?

10:15 (let [[(int a) (int b)] "xy"] ...) ?

10:15 rhickey: seems pretty easy (let [(destructure-fn a b c) x] ...), where destructure-fn takes one arg and returns a 3-tuple

10:16 arbitrary functional destructuring

10:17 fogus_: So in the original case it would be: (let [(int a b) "xy"] ...) ?

10:17 rhickey: I doubt int is going to mean that, but some fn

10:17 intify

10:17 chouser: yes, that does seem simple. That's orthogonal to the naming -- would there be 'int' and 'as-int', or would destructuring somehow call a different fn when it sees int in a destructuring form

10:18 fogus_: What would `intify` return?

10:18 rhickey: I don't think int is that intersting, also might be covered by type hints at some point

10:20 fogus_: OK, then would would `destructure-fn` return? A seq?

10:20 chouser: well, something that 'nth' could be used on, so a seq or vector

10:21 fogus_: OK. Got it.

10:22 chouser: (let [(vec a b c) x] ...) would be the same as (let [[a b c] x] ...), wouldn't it?

10:22 that seems -- right. :-)

10:23 rhickey: chouser: yes

10:23 but would make a temporary vector out of x if it wasn't already

10:24 so, kind of a waste

10:24 chouser: yes, not a practical example

10:24 rhickey: in general, (let [(foo a b c) x] ...) would be the same as (let [[a b c] (foo x)]

10:25 so, still not that interesting

10:25 chouser: more interesting when nested

10:25 rhickey: except when used in internal pieces

10:25 chouser: right

10:25 rhickey: heh

10:25 chouser: which was fogus_'s original case

10:26 had to use map on the expression side to get it, and that only worked because he wanted the same fn applied to all pieces

10:27 (let [{:keys [name (int age)]} person] ...)

10:27 fogus_: (let [destruct-fn #(vec %) (destruct-fn a b c) x] ... ) still not incredibly interesting, but fun

10:28 chouser: heh

10:28 fogus_: I could go all day like this.

10:28 * rhickey needs more tea... biab

10:31 chouser: (let [{:keys [name (Integer. age)]} person] ...)

10:32 (defn intify [x] [(Integer. age)])

10:32 (let [{:keys [name (intify age)]} person] ...)

10:33 it seems a shame to require intify be defined just to stuff a result into a vector.

10:34 (let [{:keys [name (#(list (Integer. %)) age)]} person] ...)

10:34 fogus_: yeah

10:37 chouser: but since a destructure-fn doesn't know how many values it's being required to produce, if they can ever support more than one, they all would need to return a collection even for a single item.

10:37 that would generally rule out direct use of most existing fns -- new ones would be written to be used in destructuring.

10:38 fogus_: Unless you want to create a new special type of function that returns a coll if one isn't already reurned... I doubt anyone wants to go down that road

10:38 cgrand: or change the syntax to (let [(foo [a b c]) x] ...)

10:39 chouser: fogus_: yeah, and that runs into issues when you return a coll that's meant to be a single item, so it doesn't get wrapped... bleh

10:39 fogus_: chouser: Yep. No way to know for sure

10:44 chouser: cgrand: so the existance of [] in the args would tell destructuring to expect a seq to unpack instead of a single value

10:44 lpetit: chouser: cgrand's idea fixes it, no ?

10:44 chouser: or is it the other way around ? (I guess soà

10:44 )

10:45 chouser: if foo is a function that does no collection wrapping, provide it yourself around the parameters ?

10:47 cgrand: chouser: (let [(foo x) y] ...) would always be equivalent to (let [x (foo y)] ...) no matter what x is (symbol, map, vector, another destructuring fn...)

10:48 rhickey: cgrand: and (foo a b c) not meaningful then

10:48 cgrand: rhickey: sadly yes

10:48 rhickey: but very strange to have bindings of return of foo appear as args to foo

10:48 fogus_: What about: (let [[(destr-fn a b c)] x] ... ) ? This could either mean, optionally wrap the result in a coll or always do so.

10:49 rhickey: I wouldn't want to explain it

10:49 destructuring confusing enough as is

10:49 fogus_: rhickey: True

10:50 cgrand: what about (let [([a b c] foo) x] ...)? the destructing fn isn't in fn position anymore

10:50 rhickey: maybe (-> foo a), (-> foo [a b c])

10:50 cgrand: or (let [(:as foo [a b c]) x] ...)

10:50 rhickey: the last position being the bindings

10:51 cgrand: rhickey: yes

10:51 rhickey: yes to what?

10:51 -> is nice at it chains, and leaves room for args

10:52 cgrand: yes to (-> foo a), (-> foo [a b c])

10:52 rhickey: (let [(-> foo (bar 5) [a b c]) x] ...

10:52 could do -> and ->>

10:52 djork: how about this: (let [[a b c]] (let [x (foo a b c)] ...))

10:53 and we don't make destructuring more complicated

10:53 :)

10:53 rhickey: djork: I don't understand what that says

10:54 djork: I'm just saying do destructuring in one step and them apply a fn to the destructured args in a separate let

10:54 fogus_: rhickey: Would -> and ->> therefore be the only allowed destructure-fns?

10:54 djork: would there really be special cases for onl ycertain symbols in fn-position of a list

10:55 rhickey: fogus_: possibly

10:55 cgrand: rhickey: (let [(-> foo (bar 5) [a b c]) x] ...) would be equivalent to (let [[a b c] (-> x foo (bar 5))] ...), right?

10:55 rhickey: cgrand: yes

10:56 but with the nesting support

10:56 cgrand: without nesting support it wouldn't be interesting

10:56 lpetit: The use of existing operators (-> or ->>) confuses me

10:57 What if I want to use regular -> in the (bar 5) expression ?

10:57 rhickey: lpetit: really? I think they save it

10:57 fogus_: In the original case: Would we therefore have (let [(-> int [a b]) "xy") ?

10:58 That is (let [(-> int [a b]) "xy"] ... )

10:58 lpetit: rhickey : what would (let [(-> foo (-> bar baz) [a b c]) x] ...) mean ?

10:58 rhickey: lpetit: ordinary code goes anywhere except in the last position, which is treated as a binding construct

10:58 chouser: I'm afraid it would be (let [[(-> int a) (-> int b)] "xy"] ...)

10:59 lpetit: rhickey : oh yes, it works

11:00 fogus_: chouser: :(

11:00 rhickey: chouser: or (let [(-> coerces int [a b]) "xy"] ...)

11:02 cgrand: or (let [(->> (map int) [a b]) "xy"] ...)

11:02 fogus_: cgrand: beat me to it

11:02 rhickey: cgrand: right, use functions to help us

11:03 fogus_: I like it

11:10 * rhickey declares he is not working on (-> ...) destructuring, if someone else wants to

11:11 chouser: hehe

11:12 * chouser doesn't want to document it

11:12 fogus_: I would be happy to work on it... as long as no one minds it taking until Clojure 3.0

11:17 qed: so is -> the thrush combinator?

11:17 is that rght?

11:17 right*

11:18 Txy = yx

11:23 chouser: -> is a macro

11:24 ,(-> [x (range 5)] (for (/ x 2)))

11:24 clojurebot: (0 1/2 1 3/2 2)

11:31 AWizzArd: Can deftype take a docstring? When yes: at what position?

11:34 fogus_: qed: -> is the Thrush... but because Clojure is not lambda calc it provides different forms to handle different argument positions

11:49 ,(#(* % %) (->> (range 1 100) (filter even?) (reduce +)))

11:49 clojurebot: 6002500

11:49 sh10151: Is there an idiomatic way to check for tree equality in zip-filter.xml type code?

11:50 basically I want to filter based on whether or not some child nodes are equal to a given node

12:02 qed: (recur b (+ a b) (inc term)) --> What is happening here with the b all by itself?

12:02 Chousuke: the loop has three parameters and on the next iteration the first one is b?

12:04 qed: not sure I follow

12:04 what do you mean the first one?

12:04 Chousuke: the first parameter to the loop

12:05 (loop [a 1 b 2 c 3] (recur c b a)) ; infinite loop that shuffles a, b and c :P

12:05 hm, well, b actually never changes.

12:05 qed: but you swap a and c around b

12:05 Chousuke: but a and c keep switching values :)

12:06 qed: hm, interesting, i didnt know you could control recur's order

12:06 Chousuke: order?

12:06 you just specify the values the loop will have on its next iteration

12:06 ohpauleez: qed: recur is like calling the function name again

12:06 Chousuke: bound to a, b and c in the order they were declared in the initialisation.

12:07 ohpauleez: except instead of going down the call stack

12:07 it does it in place

12:08 additionally, recur can call back to a loop, and not a function call, but the idea stays the same

12:08 Chousuke: ,(loop [a 1 b 2] (when (< b 5) (print b) (recur b (inc a))))

12:08 clojurebot: 223344

12:09 qed: oh, so it's like two nested for loops?

12:09 Chousuke: no, just a single loop with two "variables"

12:10 ohpauleez: not even, it's conceptually like writing: (defn some-name [a 1 b 2] (when (< b 5) (print b) (some-name b (inc a))))

12:10 chouser: huh. I think finished it during a single status meeting.

12:10 ohpauleez: chouser: finished what?

12:11 chouser: (let [[a (-> str b) c] [1 2]] (list a b c)) ==> (1 "2" nil)

12:11 ohpauleez: alright!

12:11 Chousuke: on the first iteration, the values are a = 1, b = 2, on the second, a = 2 (b), b = 2 ((inc a)), and on the third a = 2, b = 3, etc...

12:11 AWizzArd: chouser: Can deftype take a docstring? Do you know if it is planned that the printed representation of a deftype can be read/read-string'ed back?

12:11 drewr: ,(filter identity [1 nil 2 3 nil])

12:11 clojurebot: (1 2 3)

12:11 qed: thank you

12:11 Chousuke: that makes sense now, thanks man

12:12 chouser: ah, not quite yet...

12:13 fogus_: chouser: Wow, you're much faster than me. Clojure 3.0 is not even close

12:13 chouser: :-P

12:13 it's about 6 lines of code

12:13 Licenser: good evening everyone, how is the clojure world? Everything functioning fine I hope?

12:14 fogus_: chouser: Do you mind sending me a patch?

12:14 Or a link to a branch

12:15 chouser: it only nests in some cases, not yet all

12:16 Chousuke: the -> is not the same as the -> macro is it?

12:16 lisppaste8: Chouser pasted "-> destructuring [incomplete]" at http://paste.lisp.org/display/90584

12:16 chouser: Chousuke: very nearly

12:18 fogus_: chouser: very nice

12:19 ohpauleez: chouser: cool

12:20 cgrand: chouser: shouldn't '#{-> ->>} be #{'-> '->> `-> `->>}?

12:21 chouser: cgrand: yep, thanks.

12:22 ohpauleez: cgrand: great post in the blog, I really enjoyed it

12:22 cgrand: ohpauleez: thanks

12:25 qed: cgrand: just reading now, but while we're on the subject, i love your blog, how do you do your syntax hilighting?

12:25 lisppaste8: Chouser annotated #90584 "fixed `-> and recursive final form" at http://paste.lisp.org/display/90584#1

12:26 cgrand: qed: pygments

12:26 chouser: I think that's everything except (let [{:keys [(-> str a) b] {:a 1 :b 2}] a)

12:27 cgrand: I resurected an old pygment plugin for wordpress http://gist.github.com/213649

12:27 AWizzArd: chouser: what should your last example accomplish?

12:27 chouser: (let [[a (->> (map int) [b c])] [\a "bc"]] (list a b c)) ==> (\a 98 99)

12:27 qed: cgrand: ah i see -- i tried using pygments with my rails blog but gave up and have just been converting my code with pygments and then pasting in the html

12:28 cgrand: where'd the theme come from?

12:28 cgrand: qed: Barecity http://shaheeilyas.com/barecity/

12:28 chouser: AWizzArd: in short, the final form in a destructuring -> can now itself be a complex destructuring form

12:29 qed: cgrand: i meant the pygments coloration

12:29 my pygments highlighted code doesn't look like yours

12:29 cgrand: qed: let me check

12:33 qed: style=manni

12:33 qed: ah-ha, cool, thank you

12:35 chouser: huh. so (let [{:strs [(-> str a)]} {"a" 1}] a) can only be supported when the a in (-> str a) is a single symbol, not a destructuring coll

12:36 it's still worth doing, though, right?

12:36 rhickey: probably not

12:36 chouser: ok! then I'm done.

12:37 rhickey: wow

12:37 chouser: rhickey: you saw the patch paste? you want it in dev-clojure?

12:38 rhickey: should get an assembla entry I think

12:38 if you don't mind - thanks

12:38 replaca_: rhickey: it looks like clojure.parallel is not compatible with the latest jsr166y stuff since ParallelArray has moved to extra166y.jar

12:38 chouser: it was easy because requiring -> was brilliant

12:40 rhickey: replaca_: clojure.parallel is not going to be moved forward, as the ParallelArray is not making it into JDK 7. The new par branch will be the future of that stuff, on top of ForkJoin directly

12:41 replaca_: the other advantage of the new par approach is that it uses the persistent data structures directly, not converting to/from parrays

12:42 replaca_: rhickey: OK, so I won't worry about it for the autodoc then :-)

12:43 rhickey: replaca_: right :)

12:43 replaca_: thanks

12:43 rhickey: thank you - the doc stuff looks great!

12:46 lisppaste8: dakrone pasted "why does this script error out?" at http://paste.lisp.org/display/90589

12:47 replaca_: rhickey: thanks. I need to get it working with contrib again and then I'm going to try to get it doing multiple branches, but that's tricky for the core (since I'm using things that depend on a given version of core)

12:47 dakrone: can someone enlighten me as to what I'm doing wrong with that script?

12:49 since (ioc-file "lipsum.txt") seems to work without any problems

12:53 cgrand: dakrone: ... clojure.lang.Script ./ioc.clj -- lipsum.txt

12:54 dakrone: cgrand: if I do that, there's no output at all

12:56 tomoj: can clojure.test unwind the stack on a failure?

12:57 cgrand: dakrone: because map is lazy, here you should use doseq instead: (doseq [file *command-line-args*] (ioc-file file))

12:57 tomoj: or would I have to throw exceptions on failures and catch them at the top-level?

13:04 dakrone: cgrand: I changed it to doseq and it still errors out, same error

13:05 cgrand: dakrone: doseq and -- ?

13:05 dakrone: cgrand: okay, that did it, didn't realize I needed the -- also

13:06 lisppaste8: dakrone annotated #90589 "fixed version" at http://paste.lisp.org/display/90589#1

13:06 dakrone: cgrand: thank you very much

13:06 cgrand: dakrone: yw

13:27 dnolen: cgrand: great blog post on optimizing Life of Brian.

13:45 chouser: http://www.assembla.com/spaces/clojure/tickets/211-Support-arbitrary-functional-destructuring

13:49 technomancy: that was quick.

13:51 mtm: technomancy: playing with the latest leiningen and getting "Component descriptor cannot be found in the component repository: org.apache.maven.artifact.manager.WagonManager"

13:52 danlarkin: mtm: gah! I get that sometimes too!

13:52 stuartsierra: tomoj: clojure.test prints stack traces for exceptions, is that what you mean?

13:53 tomoj: no, I mean like how rspec stops running a test on the first failure in that test

13:53 certain failures for me mean that the test can't continue

13:54 stuartsierra: tomoj: It doesn't stop on failure. I debated whether or not to do that. But the other tests will just fail, wont they?

13:54 tomoj: well they will blow up with exceptions currently

13:54 stuartsierra: yse

13:54 *yes

13:55 So no, there's no built-in way to stop the test function prematurely.

13:56 tomoj: I guess I can just throw a meaningful exception instead of doing a test there

13:56 stuartsierra: You could write something like (deftest ... (when (is foo) (is bar) ...))

13:56 tomoj: ah, yeah, that'll work, thanks

13:56 stuartsierra: (is...) always returns the result of the assertion expression.

14:00 the-kenny: lisppaste8: url

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

14:02 hiredman: arbitrary functional destructuring <-- wow

14:02 lisppaste8: the-kenny pasted "untitled" at http://paste.lisp.org/display/90595

14:03 hiredman: binding is dynamic scope

14:03 KirinDave: Yeah

14:03 Dynamic scope is not lexical scope.

14:03 the-kenny: hm.. a bit annoying.

14:03 KirinDave: Uh, no

14:04 What you want, sir, is to use a lexical scoping construct. :)

14:04 hiredman: dynamic scope is resolved at function call time, not function definition time

14:04 KirinDave: You can visualise it as something that rides along in the call stack.

14:05 cemerick: chouser: the examples in #211 confuse me, FWIW.

14:05 the-kenny: hm.. ok. I thought something like this was possible.

14:05 KirinDave: the-kenny: It is.

14:05 hiredman: but don't do it

14:06 the-kenny: heh ok :D

14:06 hiredman: just pass an argument

14:06 KirinDave: Or swap bindings for let

14:06 Which is the lexical scoping construct. Consider what you're doing carefully though.

14:07 Because you're defining ONE instance at definition time.

14:07 the-kenny: hiredman: clojure-couchdb is a library

14:07 hiredman: sure

14:07 KirinDave: the-kenny: So you'd expect to say (bindings [*server* "server-url"] (foo))

14:08 the-kenny: In other words, you tend to set bindings at runtime at the upper level of your call tree.

14:08 hiredman: the first thing I do with libraries that use dynamic scope is wrap them in functions that take arguments

14:08 the-kenny: KirinDave: That's what I'm doing now. I was a bit confused about the defn-thing

14:09 KirinDave: the-kenny: Yeah. Just remember the idea that dynamic scope is a rider in the call stack

14:12 jasapp: hiredman: did you ever use clsql for common lisp?

14:26 chouser: cemerick: do you think that particular syntax is confusing, or the whole idea of arbitrary code allowed in destructuring? Or are they just poor examples?

14:27 KirinDave: Wow, clojure.http.resourcefully is a really nice idea.

14:28 * kotarak thinks destructuring gets a little out of control...

14:28 KirinDave: kotarak: It's just pattern matching. You haven't seen it out of control until you've worked in Prolog. ;)

14:28 cemerick: chouser: all of the above?

14:29 I'm just not clear what pain is being relieved.

14:29 for the sample examples there, or anything like it, I generally prefer (let [a (foo) a (other-foo a)] a), just to keep things clear.

14:29 kotarak: cemerick: most likely a shortened let, which I don't particular feel as pain. You get a more unreadable destructuring for return.

14:30 cemerick: exactly

14:31 cemerick: well, if I remember fogus_'s question from before that prompted this, it wasn't so much trying to destructure in a different way than it was trying to get function application into binding forms for their own sake.

14:31 although, I'm probably not being charitable there.

14:31 ...which brings me back to, when would anyone use this, why, and what is the gain?

14:35 chouser: yeah, looking at those examples again, I'm just not at all clear as to what's going on. i.e. (-> str b) is apparently *binding* b, not applying a fn 'b', which is confusing as hell.

14:42 rhickey: cemerick: are let of vectors and maps confusing in that they don't create vectors and maps? probably at first

14:44 obviously destructuring is sugar, as we can get at the components using fns

14:48 stuartsierra: I've only been vaguely following this discussion, but it looks confusing as hell.

14:48 cemerick: rhickey: [] and {} are known sugar -- start messing around with function application, and I think things go bad quickly.

14:49 It's not even so much a matter of it being confusing, the proposed change means that one needs to *read* binding-forms, whereas they're nicely scannable right now.

14:51 rhickey: cemerick: I don't see how this affects scanning (let [[a b] c d [e f]]...) 0 there's no automatic balancing op

14:52 must distinguish unfamiliar from confusing

14:52 cemerick: rhickey: I was saying that the proposal in #211 affects scanning, not existing destructuring forms (which are super-readable/scanable, IMO)

14:52 rhickey: people argue against Lisp because it is 'confusing'

14:53 cemerick: becasue you have to look at the end of (-> ...) for the bindings?

14:53 cemerick: rhickey: because you need to interpret the ... in order to have any clue as to what the bound value is

14:53 rhickey: ny example above just to show it's not clear which are the bindings without some manual grouping

14:54 cemerick: you would also if it was on the right hand side

14:55 cemerick: rhickey: granted, but I currently don't have to do any reading/interpreting to answer "what are the bindings available in this scope", or "where is foo bound", etc.

14:56 rhickey: cemerick: that, I think, is a familiarity thing, once you understand (-> ends in bindings)

14:56 I agree at the top level it is superfluous and should be discouraged

14:57 cemerick: heh, only in binding forms, but (-> foo bar) applies bar to foo everywhere else

14:57 rhickey: cemerick: but it does apply the destructuring bind of bar to foo

14:57 cemerick: vectors and maps do different things in binding forms than elsewhere also

14:58 cemerick: oh, my, now I just grokked the third example in #211

14:59 rhickey: so, when *would* this be used? I still haven't seen any compelling example where this is beneficial.

15:01 slyrus_: does stefan kamphausen hang out here?

15:01 kotarak: cemerick: still the only use case I can think of is cutting down on lets: (let [[a (-> str b) c] x] (list a b c)) vs. (let [[a b c ] x b (str b)] (list a b c))....

15:02 cemerick: kotarak: and I'd prefer the latter anyway, for clarity's sake

15:02 kotarak: cemerick: me too. :)

15:03 cemerick: I'm no minimalist, but complexity has to carry some commensurate power.

15:07 kotarak: for is a good example: (for [x abc :let [y (do-something x)] z fgh :when y w asd :while y] ...) packs a lot in the punch. (for [:let [foo bar] ...] ...) just for the sake of saving one surrounding let.... questionable.

15:10 rhickey: cemerick: I agree, and we'll have to see if it holds muster. My gut says it will have utility in those cases where you might not have direct access to the thing being destructured or the subsequent expansion, i.e. when binding forms flow through macros

15:10 spuz: cgrand: nice post today. I wonder, why does unrolling a function call get you such large performance gains? What did the original version of the code look like?

15:11 cemerick: rhickey: any example off the top of your head, what the workarounds were?

15:13 rhickey: well, for before it had let :)

15:14 the workaround was adding let to for, would we have needed that?

15:14 :let

15:14 chouser: :let is still a bit clumsy, what with the extra [] in there...

15:15 rhickey: that :let now seems like a special case workaround for exactly this, serving only one macro

15:17 it's an example of where bindings are flowing along and you haven't access intermediately

15:17 to install another let

15:17 chouser: 'let' is actually often the least compelling example for any sort of destructuring. for, doseq, and fn better

15:17 kotarak: rhickey:Well, it is only a problem in for, no? Not in let, fns, .... The only improvement there is (maybe) that you can do (let [[a (-> str b) (-> (foo b) c)] bla] ...). In which way is that better or worse than (let [[a b c] bla b (str b) c (foo c b)] ....)

15:18 rhickey: kotarak: for and doseq, and anything like them...

15:19 also a very compelling macro target, again, where you don't control the expanded-into scope

15:20 you need to perform a transformation but emit a single clause

15:22 nathanmarz: hey guys... if i have two lazy seqs, say something like (1 2 3 4 5 ...) and (0 1 2 3 4 ...), is there an easy way to "merge" them and produce something like ([1 0] [2 1] [3 2] [4 3] [5 4]...)?

15:22 cemerick: rhickey: if this *does* run the gauntlet, perhaps we can avoid -> at the very least. Having its last argument be a binding instead of the last function to be applied is perhaps the most grating issue, IMO.

15:22 kotarak: nathanmarz: (map vector seq1 seq2)

15:24 rhickey: cemerick: I disagree strongly, this is a binding form slot, where the fundamental 'function' is destructuring.

15:24 chouser: (let [(a str) 1] a) ==> "1" is perhaps more consistent, but definitely worse

15:24 nathanmarz: thank you

15:25 rhickey: reading (-> foo bar [a b c]) says to me - flow the destructuree through foo and bar before breaking up into a b c

15:26 * cemerick watches as the primacy of function position degrades slowly... :-/

15:26 rhickey: once we've taught the editors and IDEs to do local var coloring it will become clear :)

15:27 kotarak: Btw: is let in for really only a short-hand for a vector? (for [x [1 2 3] y [(str x)]] ...) vs (for [x [1 2 3] :let [y (str x)]] ...)?

15:27 rhickey: cemerick: so you'd just take parens off the table for any syntax?

15:27 one of 3 grouping constructs?

15:28 used here for function application during destructuring?

15:28 cemerick: rhickey: barring compelling use-cases, yes

15:28 of course, compelling is relative

15:29 IMO, destructuring is *structural*, thus the name. "function application during destructuring" is an oxymoron for me at the moment.

15:29 rhickey: what about :let in for, a plain workaround, the need for which will come up again?

15:31 cemerick: never having used :let in for. I tend to just use a nested let.

15:31 rhickey: you must have never needed the transformed value later in the for header

15:32 kotarak: rhickey: and even the workaround ([..]) is not impossible if really necessary

15:32 rhickey: kotarak: that is not a workaround

15:32 kotarak: rhickey: why not?

15:34 cemerick: rhickey: yeah, I rarely have more than 4 lines in a for, et al. -- a couple of seqs + a :when or :while.

15:34 lisppaste8: Chouser pasted "example of -> destructuring" at http://paste.lisp.org/display/90605

15:35 rhickey: kotarak: doesn't do the same job

15:36 chouser: I appreciate, for example, avoiding the tortured name mime-exts

15:37 Chousuke: hmm, that looks pretty neat.

15:37 cemerick: chouser: you mean (let [a (foo) a-str (str a)] ...)?

15:38 Chousuke: it might be a bit confusing initially though, given that -> and ->> are also macros

15:39 cemerick: Chousuke: no, don't you see, this will bring the locusts down from up on high!!!! :-P

15:40 chouser: but these aren't coincidentally named -> and ->> -- they're used outright in the implementation.

15:40 Chousuke: chouser: yeah, i noticed. clever :P

15:40 chouser: they mean the same, just a bit different context.

15:41 rhickey: for's :let is a perfect example of the gut case I made before, and a categoric one - should every construct that flows bindings provide a similar 'gap' to allow executable code to run?

15:41 Chousuke: cemerick: the way I see it, this just allows you to specify your own destructuring operations

15:41 rhickey: then think about a macro targeting for

15:41 that needed to emit an inner :let

15:41 aargh

15:42 Chousuke: splitting the string to mime-exts is just a part of the destructuring

15:42 lpetit: In the clojure syntax, does/can : (colon) prefix other things than keywords ?

15:42 rhickey: lpetit: no

15:43 lpetit: That is, if I want to do syntax coloring right for keywords even if just : (colon) is typed by the user, is it ok to consider it a keyword (or at least the start of a keyword) ?

15:43 Chousuke: ,(keyword? (symbol ":foo")); I think you can cheat though

15:43 clojurebot: false

15:43 Chousuke: but that's not supported :)

15:43 lpetit: I'm interested in literal keywords, btw :)

15:43 Chousuke: hmm

15:43 ,:

15:43 clojurebot: Invalid token: :

15:44 cemerick: Chousuke: yeah, I understand the mechanics. I just know that I'm confused looking at these things. Readability and consistency is a big deal for me, and I think this impacts both negatively.

15:44 lpetit: But how would you like your preferred editor to react :when you have just typed : (colon), do you want it to already apply the syntax coloration for keywords, or do you want it to apply no coloration since it's not yet a valid keyword

15:44 ?

15:45 cemerick: Clearly, I'm not the intended audience, but I'm not looking forward to reading this in other people's code.

15:45 rhickey: cemerick: I don't know why it should occur in handwritten code any more often than :let in for

15:45 but given this, we could eliminate :let in for and the need for any similar construct in any similar macro forever

15:46 lpetit: cemerick: this is a good functionality AFAIC, since it will allow me write a "warning problem marker" in eclipse if people abuse it in simple cases :-p

15:46 cemerick: lpetit: lol

15:47 rhickey: we'll have to find out, of course. :let in for is a very niche widget, though -- this will be available in every binding-form everywhere.

15:48 lpetit: Joke apart, I think if the mechanism is totally orthogonal (and it seems to be the case), then it's worth having it

15:48 But he, time will tell :)

15:49 Chousuke: cemerick: do you have any suggestions for a better syntax?

15:49 lpetit: So you did not answer my oh so more important question about the special case of handling single : (colon) syntax coloring in clojure editors ?

15:49 Chousuke: lpetit: can you have a delay in the colouring?

15:50 cemerick: Chousuke: nope, I'm just lobbing stones. I can definitely see the utility for macro writers, but I think it's simply not appropriate in user-land, so to speak.

15:50 Chousuke: lpetit: a single : is an error but you shouldn't flag it so immediately

15:50 lpetit: Chousuke: tell what you have in mind :)

15:50 Chousuke: lpetit: mostly because it's very likely to be followed soon by other characters that make it legal.

15:50 lpetit: Chousuke: cute, but no, not such sophistication for the moment (but I like it)

15:51 Chousuke: we may well end up with this later, indeed : a quick syntax coloring, and when the user pauses for a long time, a more in depth syntax coloring, yes.

15:51 ziga`: write an editor for clojure in clojure or better - an IDE with debugger and all

15:51 lpetit: Chousuke: so ok you convinced me to do nothing for the moment :)

15:52 ziga`: huh ?

15:52 ziga`: just kidding

15:52 Chousuke: for now it's more cost-effective to build upon existing IDEs anyway

15:53 though there is a Clojure editor I think. Was it called Waterfront or something?

15:53 lpetit: ziga`: there is room for anybody's contributions anyway, the door is not closed :)

15:57 cemerick: chouser: I'd suggest that if #211 is going in, we should add arbitrary fn application to map destructuring as well, if it's doable.

15:58 e.g. (let [{ival .intValue} 5.0] ival) or (let [{ival (-> :a .intValue)} {:a 5.0}] ival), etc.

15:58 chouser: it's there.

15:58 er

15:59 kotarak: (defn p [string] (for [mime-exts (.split string ",") [mime exts] [(.split mime-exts ";")]] [(keyword mime) exts])), still not sure why I need :let in for besides it being sugar.

16:00 chouser: cemerick: (let [{(-> int ival) :a} {:a 5.0}] ival) ==> 5

16:02 cemerick: chouser: ok -- I was trying to wiggle the host invocations in there. I've actually always thought being able to specify .getFoo in map destructuring would be super-useful, especially in :keys.

16:02 chouser: you can use .intValue in place of int there

16:03 cemerick: yeah, I'm talking about more straightforward usage like {a .getFoo b .getBar}, etc.

16:07 spuz: msg lpetit Hey

16:07 shit

16:07 hiredman: hah!

16:07 I knew it!

16:08 lpetit: msg spuz Ho

16:08 re -shit :-p

16:08 spuz: hiredman: that I'm retarded? I'm surprised you hadn't realised :p

16:09 hiredman: clojurebot: I knew it! is <reply>You know nothing!

16:09 clojurebot: Ack. Ack.

16:44 michaeljaaka: clojurebot: how are you?

16:44 clojurebot: with style and grace

16:45 michaeljaaka: clojurebot: what is the meaning of life?

16:45 clojurebot: what is wrong with you

16:45 mabes: it should answer with 42

16:45 KirinDave: No

16:46 mabes: Pretty sure it answered your question correctly.

16:48 mabes: KirinDave: you mean michaeljaaka's question?

16:48 KirinDave: Well, yes.

16:48 hiredman: KirinDave: very philosophic

16:48 KirinDave: It answered the question correctly.

16:57 spuz: the meaning of life is what's wrong with me?

17:03 george: clojurebot: Am i handsome ?

17:03 clojurebot: It's greek to me.

17:04 george: clojurebot: what is your name ?

17:04 clojurebot: What is meta

17:04 nathanmarz: is there any sort of clojure function that acts like reduce but outputs all the intermediate values?

17:04 fanatico: If AI has taught us anything, the correct response to "what is the meaning of life?" is "why do you say what is the meaning of life?".

17:05 nathanmarz: by output i mean returns a lazy seq to the intermediate vals

17:05 chouser: nathanmarz: clojure.contrib.seq-utils/reductions

17:05 george: or should answer it with a philosofical meta referrence ... "the meaning is to find a meaning"

17:06 nathanmarz: chouser: thanks, nice and simple like usual

17:06 hiredman: clojurebot: why?

17:06 clojurebot: http://clojure.org/rationale

17:07 george: anyone knows how is the market for clojure ... i mean , for find a job ...

17:07 chouser: george: get a Java job. Then sneak Clojure in the back door.

17:08 if that doesn't work, get another Java job -- there are plenty to choose from. :-)

17:08 ohpauleez: In Philadelphia, there are currently two companies actively using clojure that I know of

17:08 I'm pushing a third company to adopt it

17:09 george: chouser: Can i put java code in clojure as i was writing java code ?

17:10 chouser: george: not (usefully) in the same text file, but things defined in clojure and things defined in java can interact quite easily.

17:11 ohpauleez: george: and someone has created jsr233 bindings for clojure too

17:11 george: chouser: so i would have to start learning java ...=/ . It probably would be hard , after having fun with scheme ...

17:12 lpetit: george: maybe TeachScheme -> Reach Java may help : http://www.teach-scheme.org/

17:21 george: lpetit: cool ... how do i get "How to Design Class Hierarchies"

17:23 Kjellski: What is the algorithm used to coordinate things in a transaction on refs?

17:25 Chousuke: Kjellski: As far as I know it's multiversion concurrency control but I don't know the details of the algorithm at all :P

17:25 _ato: Kjellski: http://clojure.org/refs

17:25 "The Clojure STM uses multiversion concurrency control with adaptive history queues for snapshot isolation, and provides a distinct commute operation.

17:26 that means little to me, but it's enough to google some papers on :)

17:26 Kjellski: Thanks a lot... and sorry for not digging clojure.org first...

17:33 hiredman: http://citeseerx.ist.psu.edu/viewdoc/download?doi= <-- rhickey mentioned this paper on stm a while back

17:34 anyway, http://delicious.com/clojurebot/rhickey

17:34 Kjellski: hiredman : thanks... I´ll save that one too

18:02 spuz: are there any articles that describe how Clojure implements lazy sequences?

18:06 When debugging a clojure application, it's pretty mind-bending to look at the call tree that is generated

18:07 Kjellski: ,(doc .

18:07 clojurebot: EOF while reading

18:07 Kjellski: ,(doc .)

18:07 clojurebot: Gabh mo leithscéal?

18:07 Kjellski: ,(doc ..

18:07 clojurebot: EOF while reading

18:08 Kjellski: ,(doc clojure.core/.)

18:08 clojurebot: Huh?

18:08 Kjellski: ,(doc clojure.core/..)

18:08 clojurebot: "([x form] [x form & more]); form => fieldName-symbol or (instanceMethodName-symbol args*) Expands into a member access (.) of the first member on the first argument, followed by the next member on the result, etc. For instance: (.. System (getProperties) (get \"os.name\")) expands to: (. (. System (getProperties)) (get \"os.name\")) but is easier to write, read, and understand."

18:08 hiredman: . is a special form, so there is no var for a docstring to hang on

18:09 the clojure doc stuff has special handling for special forms, clojurebot's doc does not

18:09 Kjellski: hiredman : Your bot is not recognizing when closing parens aren´t out there... I´ve figured that from the sources...

18:09 hiredman: Kjellski: what?

18:09 Kjellski: ,(doc clojure.core/..

18:09 clojurebot: EOF while reading

18:09 Kjellski: ,(doc clojure.core/..)

18:09 clojurebot: "([x form] [x form & more]); form => fieldName-symbol or (instanceMethodName-symbol args*) Expands into a member access (.) of the first member on the first argument, followed by the next member on the result, etc. For instance: (.. System (getProperties) (get \"os.name\")) expands to: (. (. System (getProperties)) (get \"os.name\")) but is easier to write, read, and understand."

18:09 hiredman: and?

18:09 Kjellski: ,(doc apply

18:09 clojurebot: EOF while reading

18:10 Kjellski: ,(doc apply)

18:10 clojurebot: "([f args* argseq]); Applies fn f to the argument list formed by prepending args to argseq."

18:10 licoresse: jaha?

18:10 Kjellski: hiredman : huh? Sorry... thought that was somewhat different the last time I´ve tried that .....

18:17 hiredman: the clojurebot accepts xxx-lookup? functions without closing parens? But gives EOF while reading?

18:17 hiredman: what?

18:18 Kjellski: hiredman : Is the "EOF while reading" from the clojurebot his indication of missing parens?

18:20 Chousuke: Kjellski: it's Clojure's indication of a missing closing paren :)

18:20 try (read-string "(+ 1 2") in a repl

18:21 Kjellski: Chousuke : aha! so, nevermind hiredman ... ^^

18:23 Whatever, sleep well... =) cya

18:24 spuz: how can I tell whether a given function returns a lazy sequence or not?

18:25 say, for example, vec

18:25 hiredman: well

18:25 vec returns a vector

18:25 vectors are not sequences

18:25 so a vector cannot be a lazy sequence

18:25 spuz: I see your point

18:25 what about say, rest

18:26 ,(class (range 10))

18:26 hiredman: rest returns a sequence

18:26 clojurebot: clojure.lang.LazySeq

18:26 spuz: ,(class (rest (range 10)))

18:26 clojurebot: clojure.lang.ChunkedCons

18:26 spuz: What is a ChunkedCons...?

18:27 hiredman: spuz: why does it matter?

18:27 you can call first and rest on it

18:28 what else do you need?

18:28 spuz: hiredman: I want to know whether a function will evaluate the entire sequence that I pass to it

18:28 KirinDave1: technomancy: Hey, is there more detailed docs on how to use leiningen?

18:29 hiredman: spuz: then read the docs

18:29 spuz: hiredman: for example, vec will evaluate all the elements of a sequence passed to it, but it doesn't tell me that in the docs

18:29 KirinDave1: technomancy: Things like manipulating the classpath, etc. I'd like to use it and your clojure-http-client stuff, but figuring out the right way to go about doing that is kinda opaque to me.

18:30 hiredman: spuz: it should be obvious

18:30 vectors are not lazy

18:30 vec returns a vector

18:30 spuz: yes, but in general, is there a way to find out empirically?

18:31 hiredman: why?

18:31 technomancy: KirinDave1: docs are slim unfortunately. right now it works best with dependencies that are already published to a public repository, which clojure-http-client is not.

18:31 KirinDave1: technomancy: What's the "right way™" to use something like clojure-http-client?

18:31 technomancy: Just roll it into src/ for now?

18:32 Chousuke: spuz: try the function on a seq that has side-effects.

18:32 technomancy: KirinDave1: yes, but I hope to have a better solution in a matter of days.

18:32 you just caught me at an awkward time. =)

18:32 KirinDave1: technomancy: well I think it's an awesome library

18:32 technomancy: I know _ato is working on a public repo service that should make this easier too.

18:32 thanks

18:32 KirinDave1: technomancy: Too bad github doesn't support it directly.

18:32 technomancy: Is there an easy way to modify the invocation classpath?

18:33 hiredman: ~latex \text{vec returns a vector}\\\text{vectors are not sequences}\\\text{since vec returns a vector, and vectors are not sequences, than vec cannot return a type (lazy) of sequence}\\QED

18:33 technomancy: KirinDave1: the clean way to do it would be jar it and put it in lib

18:33 but that involves installing the clojure-pom project too, which is a hassle right now.

18:33 spuz: Chousuke: ok, but how do I evaluate the function? I can't type it at the repl as printing automatically evaluates a lazy sequence

18:33 technomancy: unfortunately the centralized maven repos are very slow-moving nad hard to get stuff into.

18:33 hiredman: undoubtly a misuse of qed

18:34 KirinDave1: technomancy: Not to mention that lein seems to disagree with the maven repo about somethign that should be there.

18:34 technomancy: KirinDave1: how's that?

18:34 spuz: heh, does anyone use QED correctly?

18:34 KirinDave1: technomancy: As lein deps on the sample repo currently fails for me

18:34 Chousuke: spuz: you can just try (do (f someseq) nil)

18:34 spuz: Chousuke: ok

18:35 technomancy: KirinDave1: there was a typo in the maven source repo URL, wipe your ~/.leiningen.jar and do a self-install again

18:35 Chousuke: spuz: but in general, most functions operating on seqs will be lazy

18:35 KirinDave1: technomancy: Ah

18:35 Chousuke: spuz: vec produces a vector so it can't be lazy. :)

18:35 spuz: Chousuke: as hiredman has proved :)

18:38 KirinDave1: technomancy: Well, okay. So… what's the way that I am to modify the classpath to include your code for tasks such as repl?

18:38 technomancy: If I have a submodule with clojure-http-client?

18:39 technomancy: KirinDave1: actually hang on; I might have this available in a public repo now that I think about it

18:41 KirinDave1: I set this up a while ago and had forgotten about it: http://p.hagelb.org/project.clj.html

18:41 but it should work

18:42 KirinDave1: rad.

18:43 technomancy: That's awesome.

18:43 technomancy: I am curious tho, how you might go about doing things like extending the classpath or other salient environment settings for the default tasks. Seems like it could be important.

18:44 technomancy: KirinDave1: I'm of the opinion that the less the classpath varies on a per-project basis the better

18:44 KirinDave1: technomancy: So, if it's not a public repo somewhere leiningen doesn't make it easy to use it?

18:44 technomancy: since any changes must be reflected in your build tool as well as Emacs or any other IDE contributors use

18:45 KirinDave1: if it's not a jar, it's going to be more complicated.

18:45 KirinDave1: For clj files it doesn't seem like it would be.

18:45 as long as you have the relative directory structures, I suppose.

18:46 technomancy: you can jar up clj files; that works fine. doesn't have to be AOT-compiled.

18:47 KirinDave1: Okay, so let's say it is a jar?

18:47 technomancy: if it's a jar, you toss it in lib/

18:47 KirinDave1: You'd lob it in deps then?

18:47 Err lib, sorry

18:47 technomancy: it's a pretty standard convention that all jars in lib get globbed onto the classpath

18:47 so all the IDEs etc. support it.

18:47 KirinDave1: I can see that.

18:48 technomancy: much of this depends on it becoming easier to publish to a public repo in the future

18:48 which I hope to help tackle once lein is somewhat stable and documented.

18:49 KirinDave1: It'd be nice if github supported us the way that they do rubygems. :)

18:49 technomancy: it would... but considering they're moving gems off github it seems unlikely.

18:49 but you know those guys, right? maybe you can talk them into it. =)

18:50 KirinDave1: Unlikely.

18:50 But I could mention it.

18:50 I see Tom a lot less because of his jet-setting successful-startup lifestyle. :)

18:50 _ato: KirinDave1: I'm have a go it: http://clojars.org/

18:51 KirinDave1: _ato: That's awesome.

18:52 _ato: I'm going to get search working today and then release this afternoon or tomorrow

18:52 KirinDave1: Wow, on the cusp!

18:52 technomancy: _ato: I'll race you to release. =)

18:52 _ato: bwaha

18:53 technomancy: wait, this afternoon? never mind.

18:53 _ato: technomancy: you decided to do a repo as well?

18:53 technomancy: _ato: that's old stuff from when I was using clojure-pom with maven

18:56 _ato: technomancy: you don't mind if I say Leiningen is the preferred way of using it right? (ie Leiningen is usable enough already)

18:56 technomancy: _ato: it's useful today if ill-documented.

18:57 the main bummer is that mvn must be installed to install your project locally since that part still shells out =\

18:57 but the primary blocker to release is documentation

18:58 _ato: ah, ok.

18:59 I'll probably write up a tutorial for clojars.org that covers the basics of Leiningen as well

18:59 technomancy: end-user type projects generally won't need to install anyway; it's only an issue for library developers

18:59 _ato: be sure to check with me before you do that; I may have something by then

19:00 _ato: ok

19:02 piccolin1: Are you going to make leiningen eventually not use Maven?

19:04 technomancy: piccolino: it will not need maven installed, but it will bundle some parts of maven with it internally. but you shouldn't have to worry about that.

19:04 piccolino: Ah, cool.

19:05 Are you going to keep it in ~/.leiningen.jar?

19:06 technomancy: piccolino: it'll probably go in ~/.m2/repository... haven't decided about that yet.

19:06 piccolino: 'k

19:07 technomancy: if you feel strongly, speak up. =)

19:07 piccolino: Oh, I don't have strong feelings about where it should be, other than that a hidden jar in the user's home directory feels wrong.

19:07 _ato: technomancy: oh yeah, that reminds me. how about a per-user config-file for leiningen, which you could add plugins (like swank or whatever) to so that you can use them without have to configure on a per-project basis?

19:07 technomancy: _ato: it will come with time, I'm sure

19:08 but I'm not going to code it myself until I need it personally.

19:08 _ato: okay, no worries

19:16 annealer: hmm

19:17 so i just noticed scrollback involving clojars / leiningen

19:17 thought i'd throw out that... https://www.javagems.org/

19:17 i'm on a similar endeavour, because maven is infuriating

19:17 gravity: Is there a standard place that people stick jars for their classpath?

19:17 annealer: it'd sure be nice if we could all work together on somethin'

19:18 _ato: annealer: ah interesting

19:18 gravity: Not having the equivalent of /usr/share/lib is weird to me

19:18 annealer: gravity: javagems installs to ~/.javagem/java by default

19:18 piccolino: Dude, I already failed on "READY"

19:18 annealer: (psa javagem is nowhere near production ready yet which is why i havent really publicised it)

19:19 gravity: heh, ok thanks :-)

19:21 annealer: piccolino: i can't tell if that was general smartassery or a slam against ruby or both!

19:21 notallama: can anyone explain why this doesn't work? (protocols) http://paste.lisp.org/display/90628

19:21 piccolino: Well, neither. I tried the READY command, and it gave me an error.

19:21 "could not find gem javagems locally or in a repository"

19:21 technomancy: annealer: would love to talk, but I can't until I get off work later today. efforts should definitely be coordinated.

19:21 annealer: piccolino: do you have gemcutter installed? some time tonight, gemcutter will become the official rubygems repo. like, 10pm tonight

19:22 technomancy: awesome. my email is gabriel.gironda@gmail.com, i'm gabrielg on github. we should coordinate

19:22 piccolino: until then, you need to "gem install gemcutter && gem tumble"

19:22 piccolino: Ah, no I do not, apparently.

19:22 I've never used gem

19:22 annealer: i figured i'd leave that part out since.... it's an official change in a few hours anyway

19:22 * _ato installs javagem to play with

19:23 piccolino: First I gotta upgrade my gem install, I guess.

19:24 annealer: _ato: as an example, something like this: http://gist.github.com/237420 works already

19:25 piccolino: Hey, I'm in Chicago too.

19:25 annealer: i'll be adding "javagem exec" tonight

19:25 piccolino: neat. we should get a beer some time

19:25 piccolino: Cool.

19:29 _ato: annealer: custom_require.rb:31:in `gem_original_require': no such file to load -- net/https (LoadError)

19:29 am I missing a ruby lib?

19:29 or wait.. do I need to run it with jruby?

19:30 * _ato tries libopenssl-ruby

19:30 Anniepoo: this is sweet

19:30 _ato: ah that looks like it

19:32 looks like it also doesn't compile clojure

19:32 but I get the idae

19:32 cool

19:32 what I like is that you can use it with C ruby so it has fast startup times

19:35 annealer: I thought about using gemcutter, but it seems important to be able to use maven-style repositories so you can use java libs without having to repackage anything

19:35 although I guess there's probably an easy way to convert maven stuff to gems

19:52 timothypratley1: http://www.wakaleo.com/blog/237-more-groovy-magic-with-maven-pom-files <<< looks promising too :)

20:48 sh10151: has anyone done anything to set up the slime repl's classpath from one that maven sets up?

20:49 danlarkin: sh10151: M-x swank-clojure-project

20:49 :)

20:50 sh10151: hey, thanks

20:50 seems like something's broken with the prompt though

20:50 it prompts for a classpath

20:50 I hit ret to accept the default

20:50 newline appears in minibuffer

20:50 danlarkin: no it prompts for a project root

20:51 sh10151: yeah, sorry

20:51 danlarkin: Ah, you have an old version of swank-clojure then

20:51 combined with not having i-do mode

20:51 (fixed bug)

20:51 tomoj: does ido not come with emacs?

20:51 sh10151: will enabling ido work around?

20:51 danlarkin: if you don't want to upgrade the workaround is to call swank-clojure-mode directly:

20:52 M-x-: (swank-clojure-mode "...")

20:52 where ... is your project root

20:53 sh10151: I'll give that a shot when emacs coms back to life

20:53 thanks for the pointers

20:53 danlarkin: it will hang forever

20:53 sh10151: yep, I think I have noticed this behavior in other modes too

20:54 I should probably just upgrade actually

20:54 danlarkin: i-do has a bug/annoying feature where if you call the ido directory chooser and you don't have ido-mode on then the minibuffer hangs like that

20:57 sh10151: hm, the clojure-update automagic seems to be confused by the git output

20:57 wish I knew the first thing about git

21:11 oof, blind update hurts ... M-x slime -> "Symbol's value as variable is void: package-activated-list"

21:12 old clojure-mode I guess

21:18 nope, still a failure

21:38 Well, M-x slime is working but M-x swank-clojure-project says clojure/main class not found

21:40 _ato: sh10151: you've got clojure.jar in yourprojectdir/lib right?

21:42 sh10151: was hoping it'd get it from pom

21:42 but looking at the implementation that appears not to be the case

21:42 _ato: I don't think swank-clojure reads the pom

21:43 sh10151: nope, just makes assumptions based on its existence

21:43 _ato: I think mvn dependency:copy-dependencies or some such should copy over the libs in the pom

21:45 sh10151: yep, it did

21:46 is clojure/main right for clojure-1.0.0 ?

21:48 _ato: not sure.. the earliest jar I've got handy is 1.0.1-alpha.. it works in that

21:49 sh10151: I think it's probably still ok and I still don't have the right classpath somehow

21:50 gonna debug swank-clojure-project to see

21:52 it has target/dependency in its list of directories

21:52 but it doesn't walk it for jars

21:52 bug or user error?

21:53 _ato: could be a bug, not sure I haven't tried it with a maven project

21:53 how about just symlinking target/dependency to lib/

21:54 sh10151: will try

21:54 every time

21:54 I have to do man ls to look at argument order

21:54 annealer: _ato: i considered the repackaging thing, and honestly, i think i'm ok with it, as long as we can automate it. which i bet we can

21:54 it seems almost trivial to turn a jar into a gem

21:54 sh10151: sorry, "man ln"

21:55 _ato: ln -s target/dependency lib

21:55 sh10151: yep

21:55 that worked, so I'm going to call it an oversight in swank-clojure-project

21:56 target/dependency is in a list of other dirs containing .class files

21:56 but it should be treated like lib

21:56 _ato: yeah, it's likely. I don't think technomancy uses maven for clojure stuff much

21:56 sh10151: wish I didn't :-P

21:57 though really for the crazy long classpaths people are installing at work, it helps a little I guess

21:57 Mule imports the world basically

21:58 _ato: sh10151: have you tried leiningen? http://github.com/technomancy/leiningen

21:58 sh10151: now that is a witty name

21:59 haven't tried it, but I will bookmark it in case I convince more people to ditch Groovy for Clojure

21:59 already have been working over a few people :-D

22:00 _ato: :D

23:15 technomancy: oh, did I miss him? dang.

Logging service provided by n01se.net