#clojure log - Jul 15 2008

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

6:41 meredydd: Hmm...so, what happens when you invoke a (ref)?

8:14 mac__: meredydd: I guess the value of the ref gets treated as a function? So if you have something else than a function in it (like an integer) it will throw an exception. Not sure I understod the question

8:14 ((ref 1)) -> error

8:14 ((ref (fn [] "Hello World"))) -> works

8:15 rhickey: mac__: what question?

8:16 mac__: meredydd: Hmm...so, what happens when you invoke a (ref)?

8:17 asked almost 2 hours ago though, might not be here anymore?

8:17 rhickey: ah, I see it now at http://clojure-log.n01se.net/date/2008-07-15.html

8:18 That behavior of refs is undocumented and subject to change

8:18 mac__: k

8:18 I just replied with what I observed :) good to know it could change

8:19 By the way, I'm a newbie lisp and functional programmer and I have a question of my own. I have a nasty loop from a java example I want to convert to clojure and I can't get my head around it...

8:19 rhickey: there was a time (prior to release) when vars and refs were the same thing, i.e. vars were transactional and you could dynamically rebind refs, but I found it to be too confusing

8:20 mac__: shoot

8:20 or maybe paste

8:20 mac__: ok, let me just paste the java code somewhere it

8:20 it's a nested loop

8:22 lisppaste8: mac__ pasted "nested loop" at http://paste.lisp.org/display/63725

8:24 mac__: tell me if you need more info there is some surrounding code too but that's the loop I'm having trouble with

8:25 util.join returns a java array with 1 or 2 shapes depending on if they could be joined or not

8:25 rhickey: mac__: nested fors map readily to Clojure's for...

8:25 mac__: sorry util.union I mean

8:25 rhickey: setting aside the fact that the body does its work by side effect...

8:26 (for [i (range (.size shapes)) j (range 1 (.size shapes))] ...)

8:28 one of the basic problems with imperative loops like that is that the logic is all gnarled up, difficult to read and understand...

8:28 so splitting it up into steps is important...

8:28 first step is making a list of items to keep/drop...

8:29 mac__: yeah tell me about it.. I spent a long time just looking at that before getting it. I've tried a few things but that innermost return haunts me

8:29 rhickey: then build a result set, which in Clojure you would do functionally, maybe with reduce, rather than mutating a collection in place

8:30 or you could just reduce from the start...

8:31 mac__: yeah I've been trying some variants with reduce but never got it right, I just couldnt come up with something that combines every item with every other item in turn

8:31 I might just need more coffee :) right now I'm stuck

8:32 rhickey: the outer structure would look like:

8:32 (reduce (fn [ret [i j]] ...) [] (map vector (range (.size shapes)) (range 1 (.size shapes))))

8:34 oops, no, I see you need every combination, so for is better

8:35 mac__: ok, I've never used for, taking baby steps here :)

8:36 rhickey: try this to get a feel for it:

8:36 (for [i (range 5) j (range 1 5)] [i j])

8:40 mac__: yeah that's nice, just the nested loop effect I need, but I'm a bit hung up on the return in the innermost loop still, how do I stop it if I find a shape union?

8:41 rhickey: if you do it in steps, you'll use the for just to return a sequence of those things you need to remove, then, after the for, you'll build a result

8:45 mac__: alright you've given me some ideas, I'm gonna try some stuff, will be back later. Thanks

8:59 cemerick: I saw the git mirror post on google groups -- FWIW, I'm *very* glad Rich is using svn at this point.

9:00 We're idly watching mercurial bubble along, but really only because it's the only dvcs that has passable tooling in an environment we use (Netbeans).

9:05 rhickey: cemerick: exactly, I need something that works in IntelliJ

9:08 Chouser: huh, that's interesting. Well, it seems to be svn is a dead-end, but I guess it'll take a while for the tools to ramp up for its replacement.

9:08 I'm entirely content using git locally talking to the svn server.

9:09 * meredydd waves at rhickey

9:10 Chouser: s/seems to be/seems to me/

9:10 rhickey: meredydd: hey

9:10 meredydd: rhickey: See the {bug/extremely obscure, undocumented and dangerous feature} report on the list this morning?

9:10 rhickey: meredydd: fixed already

9:10 meredydd: sweet, thanks

9:11 rhickey: but the 'feature' is still undocumented, might go away in the future

9:11 meredydd: yeah...that part makes me nervous

9:11 I only discovered it because I accidentally forgot to type a couple of @s in crucial places

9:12 cemerick: I'm incapable of using a vcs without a solid UI front end these days. A good sync/changes UI is a godsend compared to command-line diffing.

9:12 meredydd: and, as long as it doesn't throw an error, the unit tests don't catch anything

9:12 so if that's not something to be relied upon, any chance you could explicitly break it?

9:13 (or provide some sort of switch? Seems OTT.)

9:13 rhickey: meredydd: I haven't thought about it in a while, but if I do keep it I'll add the same capability to agents

9:14 meredydd: ooh, that'd be nice too.

9:14 rhickey: so the next step will either be to do that or remove it, but not break/flag it

9:14 meredydd: Okay, that's absolutely fine.

9:14 (and when I said "break", I meant "remove")

9:15 rhickey: I'll be in the refs/agents soon as I intend to add consistency functions

9:15 ptional validation fns to be run on change of state

9:15 cemerick: I know I've seen a function that, given a coll, returns pairs containing one item from coll and an index....can't find it now...

9:15 rhickey: optional

9:15 meredydd: Nice, rhickey...presumably just before a transaction commits?

9:16 rhickey: meredydd: right

9:16 meredydd: That would be awesome, yes.

9:16 rhickey: So transactions will be ACI_

9:16 agents too

9:17 meredydd: Well, I saw a post this morning on the list about a web framework, which mentioned a transaction macro which adds the "D" too...

9:17 rhickey: meredydd: without cooperation from the transaction infrastructure, I don't see how

9:18 meredydd: rhickey: That puzzled me, as well. I had assumed there were some undocumented hooks he was using.

9:18 rhickey: cemerick: I don't know of one in boot.clj, but is the collection a vector?

9:19 meredydd: But if you're implementing generic pre-commit triggers which can abort a transaction, I'd imagine you can make a particular ref or agent 'D' very straightforwardly.

9:20 rhickey: meredydd: to be determined

9:20 the focus is on validation

9:20 * meredydd nods.

9:22 meredydd: Finally, have you considered adding some equivalent to my 'alter-map-val' to boot.clj?

9:22 Reading and then re-(assoc)-ing map and vector values when altering refs is a PITA

9:23 rhickey: meredydd: heh, I've been waiting for people to start complaining about that...

9:23 * meredydd grins. Glad to be of service.

9:23 meredydd: I've ended up doing:

9:24 rhickey: the general issue is nested 'change', I foresee a macro like -> but that flows back, maybe <- ?

9:24 meredydd: (alter my-map-ref alter-map-val :key #(do...something))

9:24 (it's a poorly-named fn, it doesn't alter a ref)

9:25 Like (alter), I use (apply) to chain through the other arguments

9:25 so you can do something like:

9:25 rhickey: best to keep the altering separate, the need is a general one for functional nested map change

9:25 meredydd: (commute exam_marks alter-map-val "A-levels" alter-map-val "Meredydd Luff" + 5)

9:26 It doesn't do an alter - it just returns its second arg, applying the given fn and args to the value at the given key

9:27 rhickey: meredydd: just thinking out loud

9:27 meredydd: (fn [map-val key alter-fn & args]

9:27 (assoc map-val key (apply alter-fn (get map-val key) args))))

9:27 Anyway, that's my version.

9:28 (works for vectors too, of course - anyting (assoc)able)

9:29 rhickey: interesting, if a bit verbose

9:30 meredydd: And this is what I love about this language - it's created by a man who thinks that requiring one arg each for map and key is too verbose :)

9:30 I'm sure you can do better - just wanted to plant an idea in your head (where it appears it had long since taken root)

9:31 rhickey: meredydd: thinking more about the name alter-map-val than the fn/key pair idea, which is good

9:32 Chouser: alter-map-val is more tied to 'assoc' then 'alter'

9:32 meredydd: I guess you already mentioned that.

9:33 * rhickey musing... (<- exam_marks "A-levels" "Meredydd Luff" (+ 5))

9:34 * meredydd points to his comment of six lines ago

9:34 * rhickey guilty

9:35 meredydd: although (<-) had damn well better be a fn rather than a macro

9:35 rhickey: meredydd: hmmm...

9:35 meredydd: else you won't be able to pass it to itself

9:35 rhickey: ?

9:35 meredydd: (or, more likely, pass it inside something else that's expecting a function)

9:35 rhickey: Imagine my original "alter" comment

9:35 rhickey: It's symmetrical with ->

9:36 meredydd: rhickey: Uhh...is it?

9:36 What are you expecting that to expand to?

9:36 rhickey: left as exercise for the reader for now :)

9:37 cemerick: rhickey: not necessarily -- just seq-able. I could have sworn I saw such a function. Anyways, here's what I was thinking of:

9:37 (defn enumerate [coll]

9:37 (map #(vector %1 %2) coll (iterate inc 0)))

9:37 rhickey: just my intuition, I haven't written it, but it would be a macro

9:37 meredydd: my point is that it's not going to be as simple as the (->) macro

9:38 because you have to use each argument twice: once to (get) the data, once to (assoc) it back onto the original collection

9:38 so it's not going to be simply symmetrical to (->)

9:38 rhickey: meredydd: still, the associative things are consistent, so doable

9:38 meredydd: Oh, very much doable.

9:39 rhickey: meredydd: the symmetry is in the idea, walk the chain of associations, but in this case fold the last expression back into the structure

9:39 meredydd: I'm just making a case that it won't be *so* similar to (->), in advance of asking you to make (<-) a fn instead

9:39 my reason being that you can then use (alter) on it

9:40 rhickey: what if the last call needs more args?

9:41 meredydd: Oh, arse. I see your point.

9:41 (alter exam_marks <- exam_marks "A-levels" "Meredydd Luff" (+ 5)) won't fly, because the + gets evaluated too early.

9:41 But equally, it gets cumbersome if you have to make the fn explicit, because you use up your #():

9:42 (alter exam_marks #(<- "A-levels" "Meredydd Luff" #(...do something...)))

9:42 rhickey: if <- ends up being a macro, a <-@ macro is no big deal

9:42 cemerick: ah-ha -- my enumerate is seq-utils/indexed in clojure-contrib :-P

9:42 meredydd: (What would "<-@" be?)

9:42 (auto-wrap with a (fn)?)

9:43 rhickey: (<-commute exam_marks "A-levels" "Meredydd Luff" (+ 5)) ?

9:44 (<-alter exam_marks "A-levels" "Meredydd Luff" 42)

9:44 meredydd: Hmm...something in me regards it as icky to special-case both <-commute and <-alter

9:45 rhickey: oops, meant <-set

9:45 meredydd: and <-set, of course

9:45 rhickey: well, then you'll have to repeat exam-marls

9:45 makrs

9:45 marks

9:46 meredydd: not necessarily...you could do something like (warning, ugly alert):

9:46 (some-macro-name alter exam_marks "A-levels" "Meredydd Luff" (+ 5))

9:47 rhickey: sure

9:47 meredydd: and that would work when substituting "set!" for "alter" and a value for (+ 5)

9:47 rhickey: gotta run

9:47 meredydd: ok

9:47 cya

14:53 mac__: rhickey_: solved that problem I had before with the nested loop. It was part of a bigger mess so it took me a while but the for expression helped. Ended up doing something like (for [a (range 5) b (range (+ a 1) 5)] ...) so that each combination occurrs only once. Thanks for the pointer :)

14:54 rhickey_: mac__: great!

14:55 mac__: and thanks for clojure by the way, I started trying to learn common lisp but clojure hits a much sweeter spot being more functional and concurrent

14:55 rhickey_: you're welcome

15:48 cemerick: rhickey_: we had a brief data-modeling discussion some weeks back, where I was fiddling with various ideas to make a class as transparently usable from java and from clojure as possible, while keeping implementation in clojure as much as possible. Just on the face of it, what would you think of a defstruct variation that used gen-class to generate PersistentStructMap subclasses that had accessor methods for each of the privileged keys?

16:25 rhickey_: cemerick: would the keys be mutable?

16:25 cemerick: rhickey_: No.

16:27 rhickey_: cemerick: it might be interesting

16:27 cemerick: Yeah. I might give it a run next week. I'm under deadline for Friday, so all I've been able to do is think a bunch.

16:28 I need to learn a little more about how the javabean mechanisms work, because I want the resulting classes to be bean-compatible (read-only)

16:29 (ideally without blinding conforming to the getFoo convention :-/ )

16:29 rhickey_: cemerick: very easy to get if you follow the naming conventions

16:29 heh

16:29 cemerick: I *hate* getFoo.

16:30 No-arg foo should have always been the accessor, n-arg foo the setter.

16:30 rhickey_: well, if the fields are immutable you can make them public and just define the methods for JavaBeans' sake

16:31 cemerick: Yeah, that's the fallback.

16:32 rhickey_: with BeanInfo you can do whatever mapping you want

16:32 cemerick: I think I can hang a beaninfo impl off of the gen-class class that will link things up to the simpler getters

16:32 well, there you go :-)

17:24 meredydd: hey

17:25 Is there a canonical way to load a file from a resource, like generated classes do?

17:29 aha, never mind. Found it in -contrib.

Logging service provided by n01se.net