2008-06-26
| 03:34 | Lau_of_DK | Morning gents, straight to business: I need to make a JComboBox. and init it with some values. They need to be passed as a cstor argument in the form of Object(), how do I make that Object from Clojure? |
| 03:46 | cgrand | (to-array [arg1 arg2 argN]) |
| 03:58 | Lau_of_DK | thanks cgrand |
| 04:03 | cgrand | yw |
| 04:05 | arbscht | cgrand: hi, I'm curious: how did you locate my blog post with the mini snake implementation? :) |
| 04:06 | Lau_of_DK | mini snake? gimme link =)= |
| 04:07 | arbscht | Lau_of_DK: it's not that interesting http://www.plt1.com/1070/even-smaller-snake/ |
| 04:07 | cgrand | arbscht: google alert on clojure |
| 04:08 | arbscht | cgrand: ah |
| 04:09 | Lau_of_DK | impressively small |
| 04:11 | Lau_of_DK | Im having some syntax trouble. I did (def globs (ref hash-map)) for my globals, now when I want to commit a value, how do I get about it? I start firing casting errors with something like (alter globs (assoc :mykey "myval)) or in a transaction I get an invocattiontargetexception |
| 04:13 | cgrand | arbscht: submitted to reddit http://www.reddit.com/info/6p1ku/comments/ |
| 04:13 | cgrand | Btw you could flatten "collision?" by removing two of the three "and" |
| 04:15 | cgrand | lau_of_dk: no need for that many parens :-) just use (alter globs assoc :mykey "myval) in a transaction |
| 04:16 | arbscht | cgrand: hm yes, thanks |
| 04:17 | Lau_of_DK | clever man... but cgrand that fires an "hash_map cannot be cast to Associative" |
| 04:19 | cgrand | what does hash-map contains? |
| 04:19 | Lau_of_DK | nothing yet, but its supposed to contain all my global-vars |
| 04:19 | Lau_of_DK | so that I reference all the globs to it, and extract the values |
| 04:20 | cgrand | I mean: an instance of java.util.HashMap or a clojure map? |
| 04:21 | Lau_of_DK | (def globs (ref hash-map)) |
| 04:21 | Lau_of_DK | I suppose/hope that its a clojure-map |
| 04:22 | cgrand | eureka: what you wanted to write is (def globs (ref {})) or (def globs (ref (hash-map))) |
| 04:25 | cgrand | lau_of_dk: does it work now? |
| 04:25 | Lau_of_DK | hang on |
| 04:26 | Lau_of_DK | ahem |
| 04:26 | Lau_of_DK | explain it to the slow student please |
| 04:26 | Lau_of_DK | why does (hash-map) make the difference? |
| 04:27 | cgrand | (ref hash-map) creates a reference to the function hash-map |
| 04:27 | Lau_of_DK | Its a bit fuzzy to me, why does (alter bla bla assoc) work? I would image that (assoc) belong in () to evaluate it |
| 04:27 | cgrand | (ref (hash-map)) creates a ref to the result of evaluating (hash-map), ie the empty hashmap |
| 04:32 | Lau_of_DK | I understand that, but in relation to what you said about (alter ... asoc) compared to (alter ... (assoc..)) its not wholly clear to me |
| 04:34 | cgrand | alter, commute and send are functions not macros. So when you put parens around (assoc :mykey "myval") what is inside must be valid so as to be evaluated prior to the function (eg alert) call; here the map upon which you operate is missing so the content of (assoc :mykey "myval") must be break into pieces and passed directly to alter which will pu them all together with the current value of the ref |
| 04:35 | cgrand | break -> broken |
| 04:40 | cgrand | More succintly: to write (alter globs (assoc :mykey "myval)) alter has to be macro since (assoc :mykey "myval) is inomplete. |
| 04:42 | cgrand | inomplete -> incomplete |
| 04:51 | Lau_of_DK | ok cgrand, very good explanation, thanks alot |
| 08:55 | Lau_of_DK | Hey blackdog |
| 08:55 | blackdog | hi |
| 09:28 | rhickey | cgrand: *agent* type hint in place |
| 09:30 | rhickey | cemerick: added *file*, but does not use URL path, because same path is put in debug info, where jar stuff breaks debuggers |
| 09:33 | cgrand | rhickey: thanks! |
| 09:37 | cgrand | rhickey: have you seen the bug reported by wwmorgan yesterday? (sorted-set) returns an empty hashmap |
| 09:38 | cemerick | rhickey: Thanks :-) Too bad about *file* not always being absolute, though. |
| 09:38 | blackdog | cemerick, what is *file* |
| 09:38 | rhickey | cemerick: begs the question of what is absolute when it's in a jar |
| 09:39 | rhickey | cgrand: just looking now |
| 09:39 | cemerick | blackdog: path to the clj file being loaded (via load-file, etc) |
| 09:39 | blackdog | ok, thx |
| 09:47 | cemerick | rhickey: Sure -- FWIW though, file:clojure.jar!boot.clj is an absolute path, as interpreted by java.net.URL. I can certainly understand that allowing URL paths into bytecode as opposed to file paths could muck things up. |
| 09:51 | Lau_of_DK | Any of you guys know of an optimal way to approach XML handling in Clojure? |
| 09:52 | blackdog | there's an xml.clj |
| 09:52 | blackdog | but i haven't looked |
| 09:52 | Lau_of_DK | k thanks, sounds like a good place to start :) |
| 09:52 | blackdog | check api page for more |
| 09:53 | Lau_of_DK | oh goodie, Chouser has already posted some stuff on it :) |
| 09:56 | blackdog | anyone using jnlp with clojure? |
| 09:56 | blackdog | i never used it before, do i have to have a main-class tag in the descriptor when user.clj is run auto? |
| 09:57 | Lau_of_DK | (xml/parse "wayport.xml") |
| 09:58 | Lau_of_DK | lol - its a shame when your IRC client looks to similar to your REPL that you make those kinds of mistakes :) |
| 10:13 | Lau_of_DK | Am I remembering incorrectly, or was there a "loop across" functionality, that worked on hash-maps? |
| 10:16 | rhickey | cemerick: is there utility to such paths? what can you do with file:clojure.jar!boot.clj? |
| 10:24 | cemerick | rhickey: those paths aren't helpful to me, but a java debugger should have no problem yanking that resource if it's referenced in some bytecode (since remote code loading is ostensibly baked into java). The path that *is* helpful to me is an absolute path to user.clj, which would then be able to use *file* to know where it is and therefore the clojure it bootstraps from there would be able to reliably know what "root" to operate under. Right now, I'm ju |
| 10:25 | cemerick | I've been harping on the absolute path bit because the scenario I just described seems like it'd be a pretty common approach. *shrug* |
| 10:26 | rhickey | cemerick: you got cut off at Right now ... |
| 10:26 | cemerick | huh... Right now, I'm just making sure my pwd is set properly; that will get replaced by some system property at startup that user.clj will pick up. |
| 10:27 | blackdog | cemerick, you could just start the jvm up in the correct root to start with, and know where you are? then use "user.dir" within the prog |
| 10:28 | cemerick | blackdog: Yeah, that's what I'm doing now. |
| 10:31 | cemerick | There are definitely advantages to making environment concepts like "app root" and such localized to the code, rather than adding another requirement to the runtime configuration. That's definitely used a lot in the python world (for example), which has __file__, which is always the path of a file relative to your pwd. |
| 10:32 | rhickey | how is that not a classpath? |
| 10:32 | cemerick | rhickey: because when you load user.clj, *file* is _always_ "user.clj". It could be loading it from any of the root classpath entries. |
| 10:33 | cemerick | In the python example, you can always get your absolute path with os.path.join(pwd, __file__) |
| 10:34 | rhickey | Does Pythonm handle remore code loading etc? |
| 10:34 | rhickey | remote |
| 10:35 | rhickey | pwd doesn't seem a very useful concept once you get into remote code, app servers etc |
| 10:36 | rhickey | I understand classpath has its faults |
| 10:36 | cemerick | aside from some hackish workarounds, not to my knowledge |
| 10:36 | cemerick | FWIW, I'm definitely not trying to do any direct comparisons, etc. The current situation is fine, just a little inconsistent between *file* in a load-file call and *file* when user.clj is autoloaded. |
| 10:36 | rhickey | I think a current problem is Clojure doesn't use enough of the namespace management, i.e. those clj's shouldn't be in the root |
| 10:37 | rhickey | cemerick: yes, I understand |
| 10:37 | blackdog | on executing clojure from a jar file, should i include a main java stub class? |
| 10:38 | blackdog | it seems forced by the manifest |
| 10:39 | blackdog | or do i use a clojure main function somewhere to start things off? |
| 10:39 | rhickey | to get a stand-alone looking solution (i.e. no supplied script), you could, although the user.clj autoloading helps avoid that too. Note that gen-class can generate main now |
| 10:40 | blackdog | right now i'm trying java -cp clojure.jar app.jar with user.jar in app.jar and a stub java main |
| 10:41 | blackdog | i maean user.clj |
| 10:41 | blackdog | but user.clj is not (println "hello") |
| 10:41 | blackdog | not unless it's a flush thing |
| 10:41 | cemerick | rhickey: BTW, I hacked together a little macro that loads and then saves a generated class in order to get around the self-referential problem mentioned yesterday. Definitely not a long-term solution, but it gets the job done (for now, and probably only for me :-) ). |
| 10:41 | blackdog | should that work? |
| 10:45 | rhickey | cemerick: ok, sorry I hadn't thought of that self--reference in the first cut |
| 10:46 | cemerick | rhickey: seriously, no need to apologize |
| 10:47 | Lau_of_DK | naah man, we all make mistakes :) |
| 10:52 | Chouser_ | blackdog: I have gotten into the habit of always putting (flush) after any prn or println used for debugging. It makes things less surprising. |
| 10:52 | Chouser_ | I dunno if it'll help in your specific case, though. :-) |
| 10:59 | cemerick | Chouser_: damn, I had always thought that println did a flush itself. |
| 11:00 | rhickey | yeah, that seems to be a frequent trip-up for people, I'm considering changing it |
| 11:00 | Chouser_ | oh. well, I usually use (prn) for debugging. I'm not sure what println does. |
| 11:01 | Chouser_ | ...so I'm back from my trip. Have we seen a flood of new users from the 8 people I talked to on Friday? No? Ah, well... ;-) |
| 11:02 | rhickey | did you have door prizes? |
| 11:03 | Chouser_ | I guess I should have had a whole box of clojure T-shirts or something. |
| 11:03 | Chouser_ | Oh. Man. We should have Clojure T-shirts. |
| 11:03 | rhickey | there you go |
| 11:03 | rhickey | I have one |
| 11:03 | Ycros | I would buy one |
| 11:04 | Chouser_ | does it have a snappy quip? |
| 11:04 | rhickey | from my wife - it says "You're doing it wrong" on the back |
| 11:04 | Chouser_ | There you go! |
| 11:04 | Chouser_ | really, that's perfect. You would sell a dozen, at least. |
| 11:07 | rhickey | wmorgan: (sorted-set) fixed |
| 11:09 | cemerick | "You're doing it wrong" sounds good, although I'm currently feeling a little allergic to that sort of confidence, after watching a couple of noobs get torched in #lisp yesterday. |
| 11:10 | Chouser_ | yeah, it's not clear that we mean anyone *not* using Clojure. Everyone using clojure is, of course, doing it Right. |
| 11:10 | rhickey | I prefer - "State - you're doing it wrong", about which I am more confident |
| 11:11 | rhickey | but, as I said, it was a gift. Clojure is generally non-confrontational |
| 11:11 | cemerick | Sure. I'm just feeling punchy today. ;-) |
| 11:12 | Chouser_ | Maybe just flip it around to positive: "you can do better" |
| 11:16 | Chouser_ | "Who knew Java could be so fun?" |
| 11:26 | cemerick | I'm just waiting for the Clojure lolcats to start making the rounds. |
| 11:27 | rhickey | lolcats? |
| 11:28 | drewr | "Java: Makes better languages than applications." |
| 11:29 | cemerick | rhickey: here's some haskell-themed ones: http://arcanux.org/lambdacats.html |
| 11:29 | rhickey | ah |
| 11:43 | rsynnott_ | heheh |
| 12:12 | Chouser_ | http://www.zazzle.com/state_you_re_doing_it_wrong_shirt-235609552304110841 |
| 12:12 | Chouser_ | I of course haven't bought one yet, so I have no idea what the quality is like. |
| 12:13 | Chouser_ | ...and I'm happy to hand the password and stuff to rhickey so he can collect the proceeds if anyone buys any. |
| 12:34 | abrooks | Chouser_: No Clojure logo? |
| 12:34 | Chouser_ | on the front. |
| 12:35 | Chouser_ | want another on the back? i guess it might spice it up a little. |
| 12:35 | rhickey | chouser: cool! |
| 12:36 | albino | do you get to choose the font for that? |
| 12:36 | Chouser_ | yes |
| 12:36 | Chouser_ | I think you can customize your shirt however you want, from that link. |
| 12:37 | albino | I would so wear that shirt to some geek conference |
| 12:38 | Chouser_ | Since I'm using C++ at work, maybe I need one for the office that says "State - we're doing it wrong" |
| 12:38 | albino | heh |
| 12:38 | albino | give it to the managers and see if you can get them to wear it to meetings |
| 12:42 | Chouser_ | abrooks: with a logo on the back: http://www.zazzle.com/state_you_re_doing_it_wrong_shirt-235415785053901854 |
| 12:45 | rhickey | Chouser: do you need bigger images? I have 500x500 and can get larger |
| 12:45 | Chouser_ | nope, thanks. That's about 500x500 already. |
| 12:46 | Chouser_ | I've got an SVG now that seems accurate enough for whatever. |
| 12:47 | Chouser_ | I assume 500x500 is enough for that size on a T-shirt. Whoever buys the first one can report on that. |
| 12:47 | rhickey | tomhickey: any thoughts on SVG? |
| 12:53 | Chouser_ | or in black: http://www.zazzle.com/state_you_re_doing_it_wrong_black_shirt-235357523415203587 |
| 12:53 | Chouser_ | ok, that's enough. you can pick one of those or go customize it yourself. |
| 13:02 | pjb3 | http://farm4.static.flickr.com/3229/2613013337_ecc41f4123_o.jpg |
| 13:03 | tomhickey_ | http://groups.google.com/group/clojure/web/Clojure.svg |
| 13:05 | drewr | pjb3: Hahaha. |
| 13:08 | Chouser_ | pjb3: nice |
| 13:09 | Chouser_ | tomhickey_: thanks!! |
| 13:09 | Chouser_ | I should have asked for that last week instead of tracing my own. |
| 13:22 | tomhickey_ | Chouser_: no problem. glad i could help |
| 13:33 | rapido | i wonder if clojure has any distributed computation/data capabilities ? |
| 13:34 | rhickey | rapido: not yet |
| 13:34 | Chouser_ | aren't there several Java libs that can make that happen? |
| 13:34 | rapido | rhickey: i guessed that you are thinking about it. care to share some thoughts? |
| 13:35 | rapido | i believe immutability scales very well in a distributed environment |
| 13:35 | rapido | no more stale caches |
| 13:36 | blackdog | in rhickey's concurrency talk he mentions the possibility of wrapping jms |
| 13:36 | Nafai | Hi rapido! |
| 13:36 | rapido | hey nafai - the user list shows some more concatenative :) |
| 13:37 | rhickey | there are many aspects, first is some sort of process coordination/communication, for which I am looking at Shoal |
| 13:37 | rhickey | then there's distributed data/computation |
| 13:38 | rhickey | I'd definitely prefer to sit on some well-regarded Java infrastructure |
| 13:38 | rhickey | not interested in writing my own distributed DB |
| 13:39 | rapido | rhickey: i believe you only need to implement a distributed hash table |
| 13:39 | rapido | on that, you can build any data structure |
| 13:40 | rapido | for instance, you can overlay an immutable data structure on top of a dht |
| 13:40 | rhickey | we've discussed that here, but a distributed hash table is a big project |
| 13:40 | cemerick | FWIW, I'm leaning towards hadoop and HBase for our current project |
| 13:41 | cemerick | There's already a reasonably-pleasant hadoop wrapper for clojure on the google group. The data models are nicely congruent. |
| 13:41 | rapido | there are many dht's: azureus, limewire, overlayweaver, freepastry, all of them on the jvm |
| 13:41 | Lau_of_DK | Evening gents |
| 13:44 | abrooks | rhickey: As I work for an HPC company (http://SiCortex.com) I've been thinking about Clojure's concurrency mechanisms and how they might be extended across multiple nodes. It seems like the general idea behind persistent data structures could be extended but lazy fetching would lead to unacceptable latencies and there are issues of distributed GC (not unsurmountable but issues none the less). The current concurrency ... |
| 13:44 | abrooks | ... mechanisms operate on non-NUMA assumptions -- everything costs the same. |
| 13:44 | rapido | building an immutable data structure on a faulty (but redundant) dht can give you ACID properties |
| 13:45 | rapido | using some kind of MVCC or other conflict resolution methods |
| 13:46 | rapido | rhickey: so there is no need to build a distributed db yourself. just leverage on top a dht |
| 13:46 | rhickey | abrooks: It is fundamental to Clojure's model that the distributed and local models not be unified, so I expect something different will be needed for the distributed case |
| 13:47 | rapido | rhickey: i agree you can't/shouldn;t hide distance/network/etc. |
| 13:48 | rapido | like you can't hide multiple cores |
| 13:48 | rapido | behind leaky abstractions |
| 13:49 | rapido | but how should they be exposed first class? |
| 13:50 | rapido | throwing an network-exception isn't the way forward |
| 13:50 | rapido | nor hitting a stale cache entry |
| 13:51 | rhickey | I think Erlang's distributed model can't be ignored |
| 13:51 | rhickey | but it is hard |
| 13:52 | rapido | rhickey: agreed, but erlang isn't exactly p2p |
| 13:52 | rhickey | i.e. it just acknowledges that distributed programming is fundamentally hard |
| 13:53 | rapido | p2p must handle faulty nodes, faulty networks, churn, byzantine problems, etc. |
| 13:54 | rapido | you shouldn't trust no one in p2p networks! tit for tat i say :) |
| 13:55 | rapido | rhickey: distributed programming IS hard, yes. erlang has a lot of right defaults |
| 14:01 | abrooks | rhickey: Right, I wasn't suggesting that the difference between fastl/local / slow/distributed be hidden but more wondering how you saw slow/distributed fitting in with the current scheme. I could see some rich proxies of refs/agents and a mechanism proxied references for distributed GC purposes being somewhat natural fits with what we have today. One thing that shouldn't be hidden is some for of eager (or limited ... |
| 14:01 | abrooks | ... eager) caching of data in the reference proxying. |
| 14:02 | rhickey | All this is still preliminary, but here are some things I've been thinking about: |
| 14:03 | rhickey | Erlang-like procs based upon Shoal |
| 14:03 | rhickey | distributed agents also based upon Shoal |
| 14:03 | rhickey | Distributed STM based upon Terracotta |
| 14:04 | abrooks | BTW, I'm sitting in USENIX talks right now and every talk is mostly centered around the issue of "How dow we make use of multiple cores? How do we scale software in a comprehensible manner?" I keep wanting to shout out "Clojure!" |
| 14:04 | rhickey | I think Terracotta could do a good job with the data structures, but the synchronization primitives were too slow last time I tried it |
| 14:05 | rapido | shoal is more like erlang, less like p2p |
| 14:06 | rapido | trusted nodes |
| 14:06 | rapido | instead of faulty peers |
| 14:06 | rhickey | I had an earlier version of the Clojure STM running on Terracotta at one point |
| 14:06 | rapido | or malicious peers |
| 14:14 | drewr | abrooks: Ah, you're listening to Pratt in *person* :-) |
| 14:15 | cemerick | I'm still somewhat unclear about the use case for jvm-level clustering like Terracotta. "Transparent" distributed concurrency comes with so many potholes and (from what I've read) very little about data management. |
| 14:16 | cemerick | That's all probably a function of my problem space though. :-) |
| 14:17 | rhickey | it's true that once you go distributed, there is no 'one size fits all' |
| 14:18 | rhickey | some problems are mostly autonomous processes that need to communicate |
| 14:18 | rhickey | some are distributed crunching, a la map/reduce |
| 14:18 | rhickey | some are shared database |
| 14:25 | abrooks | drewr: ;-) The Pratt talk is over now. |
| 14:41 | cemerick | I suppose terracotta or shoal would help in building a durable message queue... |
| 14:41 | cemerick | it's very hard to thing outside of one's particular box when you're deep in it :-) |
| 14:42 | rhickey | yes, and there are independent solutions for all of those specifics, including message queues, so it does beg the 'what belongs in the language' quesiton |
| 14:47 | Chouser_ | rhickey: You're probably not deep into XML, but any particular reason xml/parse isn't lazy? |
| 14:47 | rhickey | maps aren't lazy |
| 14:48 | Chouser_ | but the seq of children in each map could be |
| 14:49 | cemerick | rhickey: well, the huge mass of potential solutions to these problems jumping up and down, marketing themselves as the second coming makes me think that any *language-level* support is premature. Libraries are good when things are in flux. |
| 14:49 | rhickey | Chouser: with what parser? |
| 14:50 | abrooks | Chouser_: You couldn't see well formedness with a lazy parser. |
| 14:51 | Chouser_ | sax parsers work with incremental input |
| 14:52 | rhickey | yes, but you are asking for delayed subtrees |
| 14:54 | Chouser_ | you don't think it's possible with stock Java sax parsers? |
| 14:54 | rhickey | I have no idea - you'd have to be able to checkpoint things as you passed them, as you'd have to read the subtrees anyway in order to skip them |
| 14:55 | Chouser_ | yeah. But zip and zip-filter are already lazy in this way, where they can work with parts of the tree without ever examining other parts. |
| 14:55 | Chouser_ | if the "other parts" were later in the file, it seems like it might work. |
| 14:56 | rhickey | now if xml was a chunked data format... |
| 14:56 | Chouser_ | anyway, just asking. Maybe I'll play with writing a drop-in replacement for xml/parse that's lazy. |
| 14:56 | rhickey | zip is working with a real tree |
| 14:57 | rhickey | xml files become trees, but they are not trees |
| 14:58 | rhickey | Chouser: it's a valid objective - I was just thinking about lazy-reading for Clojure this morning |
| 14:59 | Chouser_ | I do think it'd be very easy to make a zip call (and even more so a zip-filter call) that required the whole doc to be parsed, without realizing you were requiring that. |
| 15:22 | Chouser_ | ignore my earlier t-shirt links. Just use this one: http://www.zazzle.com/clojure |
| 15:22 | Chouser_ | I'll shut up about them now. :-) |
| 15:32 | Lau_of_DK | Woot - How made that print? |
| 15:32 | Lau_of_DK | How = Who |
| 15:51 | Lau_of_DK | nobody will claim responsebility? You'd make lousy terrorists |
| 15:52 | Lau_of_DK | (oops, I might have crashed an Echelon server) |
| 15:54 | Chouser_ | Lau_of_DK: I think pjb3 made the poster, if that's what you're talking about. |
| 15:55 | pjb3 | What, the automotivator? Yeah, that was me |
| 15:59 | Lau_of_DK | What is an automotivator? |
| 16:00 | Chouser_ | Lau_of_DK: http://wigflip.com/automotivator/ |
| 16:02 | Lau_of_DK | Nice work pjb3, but because with harassing the Pope |
| 16:03 | pjb3 | harassing the Pope? Are we talking about the same thing? http://farm4.static.flickr.com/3229/2613013337_ecc41f4123_o.jpg |
| 16:03 | Lau_of_DK | haha, no I wouldnt go that far about Rich |
| 16:04 | Lau_of_DK | http://images.wigflip.com/funny.jpg |
| 16:05 | Chouser_ | Lau_of_DK: I don't think that worked like you meant. |
| 16:06 | Lau_of_DK | Chouser: What do you mean ? |
| 16:06 | Chouser_ | You just posted a plain picture of the pope. Is that what you meant to do? |
| 16:07 | rhickey | The pope is OT for Clojure :) |
| 16:07 | Lau_of_DK | Yes sir... My point was this, at the wigflip.com/automotivator page, the picture of the pope is suggested, and I was just saying that something like that usually sends sparks flying - thats all |
| 17:22 | cemerick | rhickey: Back to the gen-class stuff. Would it also be possible to provide the name of the class to be generated by gen-class as a symbol? Doing so would allow gen-class to be totally independent of the state of the runtime, which would eliminate the possibility of any cyclic dependencies between Java code and referenced gen-classed classes. |
| 18:01 | rhickey | cemerick: yes, that's how it will work when I get back to it |
| 18:02 | cemerick | rhickey: fantastic. That makes life a lot easier. |
| 23:10 | Chouser | bah. so the SAX parser expects a blocking reader. It won't return until the Reader is done. |
| 23:10 | Chouser | I think that means the only way to make it lazy is to pack it off into its own thread. |