#clojure logs

2008-07-09

00:50Lau_of_DKTop' of the morning Gents
00:55hoeckLau_of_DK: good morning
01:32Lau_of_DKGents, lets say I have an entire xml file loaded in zip/xml-zip format (trees with nested vectors I think) - How do I output this to an XML file in the easiet most fool-proof way ?
02:17Lau_of_DKGents, lets say I have an entire xml file loaded in zip/xml-zip format (trees with nested vectors I think) - How do I output this to an XML file in the easiet most fool-proof way ?
02:47hoeckLau_of_DK: (binding [*out* (new java.io.OutputStreamWriter (new java.io.FileOutputStream "my.xml"))] (xml/emit x))
02:51Lau_of_DKhoeck? Have you tried that approach? xml/emit here generates garbage output with data distortion and awful formatting
02:55hoeckyes, the formatting is bad, i have tested it on a very simple xml file
03:15hoeckLau_of_DK: i forgot the (.close *out*)
03:17lisppaste8perspectivet pasted "loop-i-ness" at http://paste.lisp.org/display/63443
03:17perspectivetso I've been playing around with swt and clojure
03:19perspectivetI've been having some problems with the some of the basic functionality and I wondered if anyone had any ideas why my translation (pasted above) isn't working
03:21perspectivet(defn show-file ....
03:22perspectivetis supposed to be roughly a translation of the java snippet commented out above it
03:41perspectivetah, boom
03:41perspectivetso if I do it simplistically it works
03:42perspectivetget rid of the doto's
03:42perspectivetjust go up to a (. shell (open)) I get the expected result
03:43perspectivetoops
03:55ktnehello
03:55ktnedoes clojure have pattern matching?
03:57perspectivetin the haskell "case" sense of pattern matching?
03:57ktnei'm not familiar with haskell just with ocaml
03:57ktnematch based on type and parameters
03:58ktnecan you define for example multiple function bodies, one for each case?
03:58ktne*define
03:59hoeckktne: no, it hasn't, it has only overloading on arity
03:59ktneah
03:59ktnewhat about simple pattern matching, like a switch statement
03:59ktnedoes it have that?
04:01hoeckktne: clojure has a `cond' clause, where one can check for several expressions
04:02ktnecan you do something like this pseudocode: switch(shape) { case Rectangle(x1,y1,x2,y2): ; case Circle(x,y,radius): ; }?
04:04hoeckof course: (cond (rectangle? shape) (let [..] ) (circle? shape) ( ... ))
04:04hoeckbut clojure has multimethods too
04:06hoeckwith multimethods you are free to define you own dispatch functions
04:06ktnei'm reading about multimethods right now
04:07hoeckgood :)
04:15perspectivethoeck: found the problem I think
04:16perspectivetI'm a little unclear on let scope affects deallocation but I'm thinking that when the display goes out of the let scope it probably gets deallocated
04:17hoeckperspectivet: i tried your code, but nothing appears on my screen
04:17ktnehow do i exit clojure repl? :)
04:17perspectivetCtrl-d
04:17ktnethanks
04:17perspectivetsince if I move display outside of defn I can at least get a window
04:17perspectivethoeck: should i paste the new version?
04:18hoeckperspectivet: please
04:19hoeckperspectivet: i always read that one has to it own deallocation in swt
04:20perspectivetone has to do one's own deallocation?
04:21lisppaste8perspectivet pasted "less-loop-i-ness" at http://paste.lisp.org/display/63446
04:22hoeckoh, sorry, i mean the deallocation of the swt gui objects like buttons and so on
04:22perspectivetI'll have to read up on the top level containers like Display to see if that holds for them too
04:23perspectivetchild objects of display are fine as long as display is not in a let
04:24Lau_of_DKhoeck: Even with (. close *out*) it still mangles the XML, like "Title" becomes " Title " with excessive whitespace and linebreaks
04:24hoeckperspectivet: i see, do you have prior experience with swt or are you trying it the first time?
04:25perspectivet1st time for both clojure and swt
04:25perspectivethow hard can it be? ;)
04:26hoeck^ ^
04:26perspectivetas in see above?
04:32hoeckLau_of_DK: xml/emit looks very simple, e.g. it always inserts the `encoding=UTF-8' header as a plain string.
04:36meredyddLau_of_DK: Are you using (println) and friends? They insert spaces.
04:36meredyddEg: (println "I have" 1 "banana") --> "I have 1 banana"
04:36meredydddespite the fact that I didn't put spaces in there.
04:37meredyddwhereas (str "I have" 1 "banana") --> "I have1banana"
04:39hoeckmeredydd: emit uses (println (str ... ))
04:40meredyddWeird...
04:52lisppaste8perspectivet pasted "working-swt-snippet" at http://paste.lisp.org/display/63448
04:53hoeckperspectivet: your (second) sample works here!
04:53perspectivetit works somewhat, you still need the loop to be able to edit the text box
04:53ktneanyone here uses clojure with emacs?
04:53hoeckperspectivet: *joy*
04:53ktnei'm trying to use the clojure mode
04:53perspectivetktne: I do
04:53ktnebut it doesn't work
04:53ktnei've loaded clojure-mode.el
04:53hoeckktne: me too
04:54ktnebut the menu items are not bound to anything
04:54perspectivetmenu items, what are those? ;)
04:54perspectivetno menus on my emacs...
04:55ktnehmm
04:55ktneit looks like i had to rename my clojure script to lisp
04:55ktneclojure-mode calls lisp command
04:55ktneit works now :)
05:07lisppaste8perspectivet pasted "more useful text swt snippet" at http://paste.lisp.org/display/63449
05:07perspectivetthis one does multi-line
05:26Lau_of_DKmeredydd Im using (str xxx) for passing new children when compiling the modified tree, and then I emit that tree in a (with-out-str) nest
05:32hoeckperspectivet: no window on my machine, something weird with scope is going on there
05:40meredyddHey, this is a good one
05:40meredyddjava.lang.VerifyError: (class: clojure/fns/verdi_router/handle_connection__27590, method: invoke signature: (Ljava/lang/Object;)Ljava/lang/Object;) Inconsistent stack height 0 != 1
05:40meredyddjava.lang.VerifyError: (class: clojure/fns/verdi_router/handle_connection__27590, method: invoke signature: (Ljava/lang/Object;)Ljava/lang/Object;) Inconsistent stack height 0 != 1
05:40meredyddI think I may have stumbled upon a compiler bug.
08:35Chousermeredydd: example?
11:37ktnehello
11:37Chouserktne: hi
11:37ktneanyone using the enclojure plugin for netbeans here?
11:38ktnehi Chouser
11:38Chouserthere's an #enclojure channel
11:38ktneah
11:38ChouserI've dabbled with enclojure, but probably not enough to answer any questions.
11:38ktnethanks
11:54la_merktne: I'm using it daily at this point
12:23ktneanyone here who can tell me how to create an enclojure project?
12:23ktnei already have a source file
15:19rhickeyhi all
15:19Chouserrhickey: how's it going?
15:19Chouserrhickey: did you speak today?
15:19rhickeyGood, except the connectivity at ECOOP is not that great, so I've been offline a lot
15:19rhickeyI spoke Monday and yesterday - both went well
15:20rhickeywanted to thank every for carrying the ball here and on the group in my absence
15:20rhickeynice to see this community growing :)
15:21Lau_of_DKHey rhickey, have you been out prepping new screencasts?
15:21rhickeyI got one of the two, on the other the screencasting sw didn't capture
15:22rhickeybut I'll probably hold off on that as I'm going to do a similar talk in Sept to the Boston Lisp Group
15:22cemerickThere's a variety of new people here and there -- I personally know two developers that are using Clojure on small projects.
15:23rhickeycemerick: I'm definitely going to follow up on the gen-class load time issue when I get back
15:23abrooksrhickey: Oh, cool. When and where for the Boston talk?
15:23Lau_of_DKrhickey: ok sounds great
15:23rhickeyBoston Sept 29th at MIT
15:23abrooksrhickey: I assume that's open to the public?
15:23Lau_of_DKUuuuh MIT
15:23Lau_of_DKThe pressure is on
15:23cemerickrhickey: you mean the "recursive" classname reference bit? That'd be great.
15:23rhickeyabrooks: yes, probably pre-reg so you get dinner
15:24cemerickThat would allow me to cut out a bunch of little workarounds in our build/test process.
15:24Chouserseems like we have a pretty deep bench, even if none of us have particularly intimate knowledge of things. A few people around to answer some questions, just about any time apparently.
15:24abrooksrhickey: Good to know, thanks!
15:24rhickeycemerick: that and the perf issue
15:24rhickeyChouser: yes, that's been fun to see
15:25rhickeyHaving seen some presentations here I can tell you all that in using Clojure you are on the forefront of the multi-core oriented future
15:25cemerickrhickey: Excellent. I'd love to hear what you think is going on with the initialization performance -- I'd worried that I was on a wild goose chase, there.
15:28Lau_of_DKChouser: Did you see my posts about that xml/emit bug earlier today?
15:29ChouserLau_of_DK: I did. I think you're right -- xml/parse and xml/emit both play a bit fast and loose with XML standards regarding whitespace
15:29Lau_of_DKoh okay, i'll just write a custom emitter then
15:30ChouserI don't how much code out there relies on current behavior, but I'll look at adding some options for my lazy-xml stuff to be a bit more strict.
15:30Lau_of_DKJust wanted to make sure, that it wasn't because I was using all 10 thumbs when writing the code
15:30ChouserI think if you just take a copy of emit but without any \n, you'll be ok.
15:31ChouserYou'll have lost any whitespace on the parse side, but at least you won't include incorrect whitespace during emit.
15:31Lau_of_DKThat would one step better than what I've got now. Internet Explorer reads the xml file just fine, but xml/parse doesnt even recognize it as xml
15:32Chouserhuh.
15:33Lau_of_DKIt doubles on \n in the output file, and "Title" becomes " Title "
15:54perspectivetso I've translated an swt snippet to clojure, but the loop section is kind of gross
15:54perspectivetany ideas on how to clean it up?
15:54perspectivethttp://paste.lisp.org/display/63449
15:56erochesteryou could use (do ...) instead of (let [] ...)
15:56perspectivetah, right
15:56perspectivetjust read about do actually
15:57Chouser(when-not ...) instead of (if (not ...))
15:58Chouseryou can drop a set of parens on each method call, either (.sleep d) or (. d sleep) for example
15:58perspectivethere let me paste the newest working version
15:58Chousersame for doto as well, I think: (doto s pack open)
15:59perspectivetyeah, you're right. don't know why I did it the way i did
16:03lisppaste8perspectivet pasted "cleaned-up-swt-translation" at http://paste.lisp.org/display/63468
16:04perspectivetmuch cleaner
16:06ChouserI'm sure there would be some torturous way to replace loop with take-while, map, etc... but I really don't think it would be clearer or better for what is an inherently non-functional API.
16:07perspectivetyeah, my thinking also
16:11slavawhen writing guis in a functional style you want to use something like the builder pattern...
16:12slavainstead of passing the parent widget to the child's constructor, have the parent constructor take &rest args
16:13slavaand replace setters with a map that you pass in
16:13cemerickWe're biting the bullet and just using Netbeans/Java for our UI. It's decidedly "non-core", so we're going to do it wrong from the start. ;-)
16:14Nafaislava: Sounds nice.
16:15slavasomething shoes would work well in clojure i think: http://code.whytheluckystiff.net/shoes/
16:15slavaespecially since most of the hard work is done for you by swing
16:15NafaiSwing is painful at times :)
16:16slavaor swt or whatever the flvaor of the month java gui is :)
16:16NafaiThat's my day job right now, developing a Swing component
16:16slavamsut be a big component if that's your entire job :)
16:17NafaiIt's a fancy grid / table component
16:18perspectivetisn't swing performance still kind of lacking these days? at least compared to swt?
16:19NafaiI haven't found such; I used to use SWT at my last job.
16:20perspectivethow's the native look-n-feel these days? It was pretty bad last time I looked?
16:20slavafrom what i've seen lately, swing is starting to look more native than swt :)
16:20slavaswt needs a big overhaul on os x to convert it from carbon to cocoa
16:21perspectivetthat's underway though from what I've seen
16:21perspectivetNafai: so you think swing is the way to go for a graphics heavy java app?
16:22slavawhat is swt's story when it comes to opengl?
16:22slavais it supported as well as swing?
16:22perspectivetwith jogl panels and such
16:22slavagood
16:22perspectivethttp://www.eclipse.org/swt/opengl/
16:24Nafaiperspectivet: I'm not sure how I feel. Both of the choices for SWT/Eclipse and Swing were handed down from up-above.
16:24NafaiAn advantage of Swing is there are a lot more 3rd party things
16:27perspectivetYou don't need to ship swt jars either
16:27perspectivetwhich is nice
16:46jgrantin the context of thread local variables...
16:46jgrantI've tried 2 approaches
16:47jgrantthe first being (def x) (binding [x ...]) (set! x ...)
16:47jgrantthis works but i know that this apparently not idiomatic of clojure
16:47jgrant*winks about the exclamation in set! *
16:48jgrantthe other is (with-local-vars [x ...] (var-set x ...) (var-get x ...) )
16:48jgrantthis second approach is very tedious
16:49jgrantand exceptions are thrown a local var is access but not yet set meaning even more exception handling code
16:49jgrantis there another less verbose way to handle thread local variables ?
16:50Chouseryou want to mutate the variable, not just stack bindings I guess?
16:50jgranti do not need to mutate the root var just looking for thread isolation
16:51jgrant(def x ...) is not thread-safe obviously from what i have seen
16:53ChouserI think your first form is actually correct.
16:54Chouser(set!) is documented for thread-local bindings: http://clojure.org/vars
16:54ChouserI think the exclamation is to signal mutation, which indeed is happening within the thread.
16:55jgrantyea it's much cleaner but i recall rich saying the set! should never be necessary in one of his talks but i'm not sure how else to do this ?
16:56jgrantdoing var-get on everything is ugly and error prone
16:56cemerickAm I wrong in thinking that binding does exactly this?
16:56cemerick...exactly what you want, I mean. :-/
16:56jgrantdoes what ?
16:57cemerick"Bindings created with binding cannot be seen by any other thread." http://clojure.org/vars
16:57jgrantyes i set! works and it's cleaner but why would rich mark it with the exclamation ?
16:57jgrantyes bindings are thread local
16:58jgrantwhat is dangerous about set! if it's only modifying a thread local variable ?
16:58cemerickjgrant: Oh, because mutation like that is Bad (tm).
16:58jgrantwhy is it bad ?
16:58erochesterjgrant: Chouser's right. The exclamation just signals that the function mutates something. It's a convention from scheme and other lisps.
16:59erochesteri think bad = makes concurrency hard, and isn't a functional way of approaching a problem
16:59cemerickjgrant: It's a stylistic indication that if you're using set!, you're probably not taking advantage of other, cleaner primitives.
16:59jgrantcemerick : have you tried using (with-local-vars (var-set ...) (var-get)) in thread safe code ?
16:59jgrantwhat are those 'cleaner primitives' ? (I've asked a few times now)
17:00cemerickjgrant: I've not personally used with-local-vars, no.
17:00jgrantcemerick : (withlocal-vars) is hardly clean
17:00jgrantit turns my code into an eyesore
17:00cemerickan example of a cleaner primitive: set! is often used to manage state within a loop, etc. It's far better to use loop/recur in that scenario.
17:01Chouserset! will throw an exception when used without a thread-local binding. I think it's safe and valid.
17:01jgrantloop/recur has nothing to do with thread-local vars
17:01ChouserYou shouldn't be doing any mutation at all if you can help it, but if you must, I think using set! within a thread isn't particularly bad.
17:01jgrantChouser : i agree
17:02cemerickjgrant: strictly speaking, no, it has nothing to do with thread-locals, but in the case of iterative processing, loop/recur is way better than doseq/dotimes + set!.
17:02Chouserjgrant: you're sure you need to mutate, and can't use recursion or stacks of binding calls or anything?
17:02jgrantcemerick : how specifically is loop/recur better than doseq/dotimes + set! ?
17:02jgrantanyone care to define better ?
17:03Chouserno mutation.
17:03Chouserwith loop/recur, a local gets it's value at the top of a block, and then doesn't change until the block is done
17:03jgrantif this was common lisp then i wouldn't be asking the question ;-)
17:03erochestercommon lisp isn't really a functional language :)
17:04cemerickjgrant: the same way using foreach over an enumerable is better than iterating through it manually with counters in C#/Java -- more idiomatic, and there's the small bonus of having no side-effects
17:04jgrantin real life no 100% pure functional language works
17:04Chouserwith any kind of mutation (including set!) you have to scan through all depths of the code indentation, and possibly even within called functions to see who might be calling set!
17:04jgrantcemerick : your're assuming that what is being modifed needs to be looped over but in many cases it does not
17:05cemerickjgrant: Yes, I was citing one example.
17:05ChouserI don't think there's a real argument to be had here. set! is provided for thread-local bindings because it is needed sometimes.
17:05jgrantChouser : thanks that's what i needed to know ;)
17:05cemerickChouser: agreed, it's a handy backdoor for those very, very rare cases
17:05ChouserOften mutation (using set! or def) is done when it would be better not to.
17:06erochesterit's handy for global parameters, like common lisp uses dynamic variables.
17:06ChouserClojure provides one or two "backdoors" compared to a purely functional language, but it provides a whole pile of features to make it easy to avoid mutation entirely.
17:07Chouserjgrant: if you're willing, it'd be interesting to see your use case. Maybe somebody can come up with solution that is syntactically cleaner than set! or var-set, and also mutation-free.
17:51ericthorsenIs there a shortcut for (apply merge (;sequence of maps))? Similar to vec and vector?
17:53ericthorsennevermind...(reduce conj maps)
18:29jgrantChouser : the use case is really a thread that generates a string based on some variables (i.e. it looks up values in a hash map) so there really is no looping
18:31jgrantmost important is that the variable that stores the string is thread local and that it can be modified over and over as the string is being generated and then finallly return
18:33jgrantusing with-local-vars and var-set var-get turns this into very verbose code
18:37slavatomhickey_: are you related to rich? :)
18:38tomhickey_slava: yes, he's my brother
18:38slavacool
18:38jgranttom : are you also involved in the clojure implementation ?
18:39slavawill you guys start a company Hickey & Hickey LLP to commercialize Clojure? :)
18:40tomhickey_jgrant: no. but i do develop with it
18:41tomhickey_slava: =) that's all in Rich's hands
18:42jgranttomhickey : cool, tom do you have any advice for me on my use case above ?
18:44tomhickey_jgrant: i wish. i think Chouser and cemerick provided much more help then i would be capable of
18:45jgranttomhickey : thanks yea they did
18:47StartsWithKWhy i can't get function medadata this way? (defn x [a] (println a)) (def get-meta [f] (meta (var f))) (get-meta x) But (meta (var x)) outside of a function works.
18:48jgrantslava : it's my understanding that rich will keep clojure open it's currently under the CPL v1.0
18:49jgranthttp://www.opensource.org/licenses/cpl1.0.php
18:53jgrantlooks like this has gone up recently --> http://clojure.org/state
18:53jgrant(i dont remember it being there before)
18:57jgrantthe last sentence on that page says this : "In the local case, since Clojure does not have mutable local variables, instead of building up values in a mutating loop, you can instead do it functionally with recur or reduce."
18:57jgrantSo the question is what if the mutation is not in a loop ?
19:04Chouserjgrant: you might consider using the StringBuilder class.
19:05ChouserBut that's really just another form of the same "cheat", shoving the mutation down into a Java.
19:05jgrantyes that is something i'm considering
19:06ChouserSo you're doing a lot of (set! foo (str foo "bar")) ?
19:07jgrantyes
19:08Chouserso you append "bar" and "alice" and "cindy". You might consider (str "bar" "alice" "cindy") instead.
19:08Chouser(yes, I realize this is a drastice over-simplification that might be hard to implement)
19:08jgrantand if there is logic between "bar" "alice" and "cindy" ;)
19:09jgrantChouser : but thanks i see where you're going with this
19:10jgrantmaybe i can convince rich to take the exclamation out of set! ;-)
19:10Chouserright. (str (let [...] ... (apply str (map ... "bar"))) "alice" (reduce ....))
19:10jgrantafter all set! is for thread local variable modification it does not modify the root context
19:11ChouserI really think the ! just indicates mutation. The ruby standard library is full of methods differentiated by ! for whether they mutate their object or not.
19:11jgrantChouser : you are right by those are methods on classes and set! is a function/macro
19:11jgrantso slightly different meaning
19:12Chouserwell, if you say so. It seems to me they both indicate that the thing their operating on will return a different value afterward than they did before.
19:12jgrantChouser : yes you're right
19:12jgrantbut why dont we have def! or var-set!
19:12jgrant?
19:12jgrant(in clojure)
19:13jgrant;)
19:13jgranteven more of a reason for def! because it modifies variables that are not thread local
19:15Chouserindeed. but you're not supposed to use def anywhere much.
19:15Chouserbathtime for kiddos. bbl
19:16jgrantagain it is necessary in apps that aren't threaded but have the same requirement as we've covered above with string building.
19:16jgrantAnyone raise your hands if you're ok with def or set! being removed from clojure ;-)
19:18jgrantyes we all agree imperative programming is 'evil' but a 99% functional 1% imperative language is still necessary for building real-world apps
19:26jgrantespecially since clojure is built on java and takes advantage of existing libraries.
19:29meredyddjgrant is right, I think, about the exclamation point - it's intended as a "whoa...are you *sure* this is necessary?"
19:29meredyddI think you're dead wrong about state, though :)
19:30jgrantmeredydd : how would you solve the problem described above with conditional string building ?
19:30meredydd*looks*
19:30jgrant(in a purely functional manner)
19:30meredyddWhat's the problem?
19:31meredydd(sorry, didn't grok what you were trying to say)
19:31meredyddcould you state it again?
19:31jgrantsure...
19:31jgrant...a thread is generating a string stored in a thread local variable based on some conditions
19:32meredyddah, and this is assumed?
19:32jgrantthere's lots of concatenation going on
19:32meredyddBecause if you're taking, as your starting assumption, that there is:
19:32meredydda) a thread, operating sequentially
19:32meredyddb) storing its results in a thread-local variable
19:32jgrantok lets use a real world example
19:32jgranta web server
19:32meredydd...then yes, you're going to end up requiring imperative programming, because you've already assumed it :)
19:32jgrantwe dont want to send all client the same payload right ?
19:33jgrantok then do you have another solution ?
19:33meredyddOkay...what parts of a web server?
19:33meredyddWell, depends what the string generation is trying to *do*.
19:33jgrantservicing a request
19:33jgrantand returning a payload
19:33jgrantit's a dynamic payload based on params passed in to the server
19:33meredyddUhh...so, to put it another way, the payload is a function of the request and its parameters? :)
19:33jgrantvery simple
19:34jgrantyes
19:34jgrantever used a function (in a mathematical sense) that modified a paramter ?
19:34jgrantit's very common
19:35meredyddSo, generally speaking, you'll want to write a function which takes the request as a parameter, and returns the response.
19:35meredyddReally, jgrant? Give me an example.
19:35jgrantso everyone states i'm wrong (which i have no problem with) but no one has a solution yet ?
19:36meredyddIt's difficult to have a discussion, when what you're essentially saying is "I need to do <procedural programming implementation technique>, so I need to use procedural programming!"
19:36jgrantis that what i'm saying ?
19:36meredyddMy hunch is that whatever underlying problem you're solving can *also* be decomposed in a functional way.
19:37jgranti'm open to any solution presented in fact i would love a purely functional solution instead
19:37meredyddFor example, by the time you're thinking "servicing a request" = "generating a thread-local variable in another thread", then you've already committed yourself to a procedural approach.
19:37meredyddSo, present the problem :)
19:37jgrantok forget the thread-local variable
19:37meredyddWhat does this web-server need to do?
19:39jgrantcommonly what you already described, i.e. serve a payload based on incoming parameters
19:39jgrantwhat most web servers do
19:39meredyddokay...in that case, I'd do something like:
19:39jgrantmereydydd : have you looked at web server implementations in functional langauges ?
19:39meredyddmain loop:
19:40meredydd(loop [] (let [s (. my-server-socket (accept)] (new Thread #(handle-connection s))) (recur))
19:41jgrantyes that's not the problem though
19:41jgrantis generating the payload in the thread that's a problem
19:41meredydd(defn handle-connection [s] (.write s (generate-response (read-request s)))
19:42meredydd[note that I'm hideously mangling the ServerSocket API here - because of how Java works, there's a bunch of (.getOutputStream) and friends needed, but they don't affect the main point)
19:42jgrantnow we're going somewhere, and what would generate-response implementation look like ;)
19:42meredyddokay, fudging the generation of a filesystem path from a request (which I think you'll agree is pretty easy to do as pure-functional):
19:43jgrantnot concerened with any of that stuff
19:43jgrantit can all be handled purely functional
19:43jgrantit's generating the payload i.e. string concatenation
19:43meredydd(defn generate-response [file-path] (let [finp (new FileInputStream file-path my-response (read-all-from finp)] my-response))
19:43jgranthow would you do it without def or set! ?
19:44meredyddWhat kind of string concatenation are you after?
19:44jgrantaha !
19:44jgrantthat would just load a static file form the filesystem
19:44jgranti'm talking about generating a dynamic response
19:44meredyddyeah...you asked for "what a web-server normally does"
19:44meredyddAll right...what do you want it to do?
19:45jgrantlet's say you have a template
19:45jgrantand values that will be used to populate it ?
19:45meredydd(defn generate-response [request] (str "<html><body>Hello, " (:name request) "</body></html>")
19:45meredyddthe "template" there is in S-expressions
19:45jgranthow would you make parts of the template conditional ?
19:46meredydd(defn generate-response [request] (str "<html><body>" (if (:name request) (str "Hello, " (:name request)) (str "Enter your name: <input name='name'...")) "</body></html>")
19:47meredyddThat part's conditional on whether you specified a :name parameter (assumed to be grabbed from the HTTP params)
19:47meredyddIf you wanted to load the template from a file, you could do something like:
19:48jgrantnow what if the request needs to be 'cleaned' before being used ?
19:48jgrantwhere is that stored ?
19:48meredydd'cleaned' how? Shall we just say a functional cleaning?
19:49jgrantvalidation of incoming params
19:49meredydd(defn generate-response [dirty-request] (let [request (clean dirty-request)] ...code here...))
19:49jgrantdecoding of values passed in
19:49meredyddAh...and now I understand what you mean by "changing the arguments".
19:49jgrantand is let thread-safe ?
19:50jgrantfrom what i understand let is not
19:50meredydd"mu."
19:50meredydd(let)-bound variables are lexical, and never change value.
19:50meredydd*lexically-scoped.
19:50meredyddahh...here's our confusion.
19:51meredyddMy assumption here is that (clean) doesnt' modify the "request" object.
19:51meredyddbecause the request object is immutable.
19:51meredyddInstead, what it does is return a new "request" object, whose value is a cleaned-up version of its argument.
19:51meredyddThis is how the Clojure collection functions work, as well. For example:
19:52meredydd(let [a {:name "meredydd} b (assoc a :name "jgrant")] (println "a:" a "b:" b))
19:52jgranti see
19:52meredyddprints "a: {:name "meredydd"} b: {:name "jgrant"}"
19:53meredyddthe value of "a" never changes - (assoc) just returns an immutable map which differs from the immutable map it was passed only in the value bound to the key :name.
19:53jgrantso we trade memory for going purely functional (which is what rich described on the state page of clojure.org)
19:54jgrantnow in a large web server we end up using LOTS of memory the more clients we have connecting
19:54meredyddAs a rule of thumb, when your procedural experience tells you "I need to change the value of this variable", The Functional Way is "I need to declare a new variable which is some function of the value I currently have"
19:54meredyddAaah...yes. And this is where persistent collections come in.
19:54meredyddFor example, if I did:
19:54meredydd(let [a {:name "meredydd" :data <lots-of-big-ugly-data>} b (assoc a :name "jgrant")] (println "a:" a "b:" b))
19:55meredyddthe value of "b" *shares* its memory storage of <lots-of-big-ugly-data> with "a".
19:55jgrant'persistent' being a euphemism for imperative then ;)
19:55meredyddnope.
19:55meredyddso you've only allocated enough new memory for the new "name" value
19:55jgrantpersistent objects are mutable correct ?
19:56meredyddNope. Immutable.
19:56meredyddb is immutable, as is a.
19:56jgrantthen they cannot be changed
19:56meredyddcorrect.
19:56meredyddBut when you create b, using a as your argument to (assoc), you re-use as many bits of "a" as you can get away with.
19:57jgrantso we use more memory
19:57meredydd(and you can get away with a lot - because the value of "a" won't change, you can re-use all its data structures!)
19:57joubertwell, if you want to store more information you need to use more memory, no?
19:57jgrantwhy when it's just a modified copy
19:58jgrant?
19:58meredyddjgrant: That's the point - you don't. You only allocate enough new memory to hold the new entry you just created (in this case, ":name --> 'jgrant'")
19:58meredyddIt's not a "copy", like you're thinking of.
19:58jgrantif i have 32GB of data and use functional approach instead of the 'evil' imperative approach i could theoretically have to use 64GB instead
19:58meredyddHold on one moment
19:58jgrantmaybe not to the programmer but to the machine it is a copy
19:59meredyddOther way round, actually - the programmer can treat it as a copy
19:59jgrantor at least a modified copy
19:59meredydd(which is why I explained it to you that way)
19:59meredyddbut to the machine it's much more efficient than that.
19:59jgrantthe point is the functional approach requires significantly more memory
20:00dudleyfThe machine only has to keep track of the "diffs" between the original structure and the new one
20:00meredyddYes, and I'm arguing that that point is incorrect.
20:00jgrantdudleyf : thanks
20:00jgranthadn't heard that yet
20:00joubertjgrant: that is exactly what meredydd was saying :-)
20:01meredyddhttp://www.skrbl.com/79556388
20:01meredyddjgrant: I'm going to draw you a diagram.
20:01jgrantmeredydd : thanks
20:02joubert(btw, there's also a great presentation at http://clojure.blip.tv about persistent, immutable data structures)
20:02dudleyfRich has some interesting diagrams on how the data structures actually work in one of his presentations
20:02dudleyf:-)
20:02meredyddoh, that'd be marvellous, dudleyf.
20:02meredyddCause this interactive-whiteboard app is really killing me.
20:03meredyddjgrant: Have you opened that link?
20:06jgrantmeredydd : yes looking at it now
20:06meredyddokay. You see the map value "A" on the left?
20:07jgrantyes
20:07meredyddIt has two entries, ":name --> meredydd" and ":data --> big-chunk-of-data"
20:07meredyddit contains pointers to each entry
20:07jgrantright
20:07meredydd(and yes, I know how rich actually does this...this is a simplification, because the implementation details aren't important at this stage)
20:07jgrantsure
20:08meredyddNow, what that (assoc) function does is to make a new entry, for ":name --> jgrant"
20:09jgrantis see
20:09meredyddNow, it creates a map value "B", and puts in the pointers I'm drawing in red.
20:09meredyddSo, A is still immutable - I haven't *changed* the value of any existing variable.
20:10meredyddBut at the same time, what I have is a map that uses the same chunk of RAM to store our BIG_CHUNK_OF_DATA
20:10jgrantso A is updated with the existing values in A and the new ones in B ?
20:10meredyddbut has a different value for the :name key.
20:10meredyddA isn't touched at all.
20:10meredyddThere's no "updating">
20:10jgrantso how then is :name resolved correctly ?
20:10meredyddAll I did was create a new map, with the pointers in red.
20:10meredyddWell...
20:11Chouserin the webserver example, A would be the uncleaned parameters, B might be the cleaned ones.
20:11jgrantah i see
20:11meredyddIf I look up :name in A, it'll look down the blue pointers and find the blue entry
20:11ChouserIf you want the cleaned ones, you look them up in B instead of in A.
20:11meredyddthanks, Chouser.
20:11Chousermeredydd: using skrbl looks like a lot of work. ;-)
20:11meredyddChouser: You don't say.
20:12jgrantbut i have 2 refs now
20:12meredydduhh...
20:12meredyddbetter use a different word - "ref" has a specific technical meaning in clojure.
20:12meredyddyou have two bindings, yes.
20:13Chouserone other little point -- if you stop using the uncleaned params (A), the JVM reclaims that memory (the little bit where :name->"meredydd" was stored) and it gets used for something else.
20:13meredyddChouser: I was just about to explain that :)
20:13jgrantso each time a modify something i have to deal with a new ref
20:13jgrantbinding
20:13jgrant;)
20:13meredyddjgrant: Yes. As I say - when imperative stuff thinks "I need to change this value"
20:13Chouserjgrant: only if you want to. Do you want the cleaned ones? use those. The new object has new meaning.
20:14meredyddfunctional stuff thinks "I need to make a new variable, with a slightly-changed value"
20:14jgrantyea just thinking in my specific case
20:14jgrantif i modify something x number of times then i have x number of bindings
20:14meredyddyep.
20:14jgrantx1 x2 x3 x4 etc etc etc
20:15jgrantsimply because i cannot rebind the new value to the existing symbol
20:15meredyddThat's just a style thing. You actually don't need to do that many changes in sequence, under most circumstances.
20:15Chouseryes, but there are library of functions to make such multi-step "modifications" less necessary.
20:15meredyddIf you find yourself doing that, you generally ought to look and see if you can do it more simply.
20:15meredydd(or, as Chouser says, use a library function)
20:15Chouseranother detail is that if you really really want to, you can use the same name for the new object:
20:16Chouser(let [a 1, a (+ a 2), a (* a 5)] a) would return 15
20:16meredyddjgrant: Also note that if you're doing: (let [x1 (do-something x) x2 (something-else x1) x3 (and-again x2)] (...do something with x3))
20:17meredyddjgrant: ...in that case, the virtual machine will notice that you're not using x1 or x2 any more, and deallocate them
20:17meredydd(I'm drawing on the diagram, in green, what that would mean in our example)
20:18jgrantyea i get that
20:18meredyddjgrant: So, you don't use the extra memory.
20:19jgrantso how would you do conditional assignment of vars ?
20:19jgranti.e. some vars are only set if others have certain values
20:20meredydd(let [x (if (sky-is-blue) "blue" "not blue")] ...do something with x...)
20:21meredyddIf you're used to working in a language with a ?: operator, like Java or C, you'll be used to this kind of thing
20:21jgrantyes that makes sense
20:21meredydd(the Java equivalent is: { String x = sky_blue()?"blue":"not-blue"; })
20:22meredyddexcept that, this being a functional language, we have *much* more powerful tools than ?:
20:23meredyddWe can use (cond), for example, which is like a turbo "if-elseif-elseif-elseif" version of ?:
20:24jgrantyea my main source of confusion has been using existing Java libraries (3rd party) and trying to convert the example code to idiomatic clojure code
20:26meredyddah, yeah. That's definitely a problem.
20:26meredyddMy personal advice would be to practice with pure clojure code
20:26meredyddGet your functional kung-fu up to scratch - get inside the mindset.
20:26meredyddThen, once you understand *both* sides of the fence, you can bridge it efficiently.
20:27jgranti'd love to but i'm set on using clojure right now while i'm coming up to speed , i'm blown away at how productive it is even while learning
20:27meredyddOtherwise, if you only know one side, you'll end up trying to write Java in S-expressions
20:27jgrantvery true
20:28meredydd(which is all right as far as it goes - as you say, it's still a massive leap over plain Java - but you'll be missing out on a lot of the fun stuff too if you do that)
20:28jgrantso when does the O'Reilly clojure in a nutshell come out ;) ?
20:28meredyddIn that case, if you can, try to keep your Java interface code neatly segregated into a few functions.
20:29jgrantwell what i find is that even the kludgey code i have to come back and re-write usually doesn't take very long once i discover the right way
20:29meredydd:) yep
20:29meredyddI recommend you make a religious point of never, ever using (set!), until you're really comfortable with it.
20:30meredydd(set!) is a way of bending the rules, and once you get a feeling for the functional mindset (it won't take long), you'll have a good idea when to break the rules, and when there's a perfectly good functional way of doing it.
20:30jgranti know set works with (binding [...]) does it also work with let or does the binding have to be mutable ?
20:31meredyddIt has to be a (binding).
20:31jgrantok good
20:31meredyddAs I can't think of anything I've done in my last ten thousand lines of code that would *need* a mutable binding, I'm inclined to recommend you forgo them altogether until you're used to working with (let)
20:31jgrantmeredydd : thanks alot for explaining all this
20:31meredyddnp.
20:32meredyddChouser helped a lot, too - that "diff" comment was a one-line version of something I'd been trying to explain for ten minutes :)
20:32jgrantChouser : yea thanks too !
20:37meredyddRight.
20:37meredyddAnything else I can help you with?
20:37meredyddcause I've just come back from London, and am in the mood for a good night's sleep :)
20:38jgrantmeredydd : thanks, i'm sure i will have some more questions but get some sleep.
20:38meredyddOkies.
20:47Chouser"diff" was from dudleyf, but thanks anyway!
20:47jgrantChouser : but yea i owe you anyway ;)
20:49Chouserit's a hard transistion to make, so it's great you're willing to put in the effort.
20:52jgrantChouser : i've spent too much time with CL
20:52jgrantit's bridging the java libs with clojure idioms that's getting me
20:52Chouseryeah, Java and its libs definitely push you toward mutable state
20:52jgrantbut i must say there is a great community around clojure
20:58ChouserI think so too. I wonder how that happens. Some languages and projects just don't -- you ask the wrong question and you're treated like some kind of idiot.
21:03cemerickChouser: is the archive you maintain of #clojure kept by a bot, or is that just your personal transcript of the channel?
21:04ChouserIt's collected from the connection I speaking over right now -- irssi, not a bot.
21:04ChouserIt's converted to HTML by a clojure program.
21:05wastrelwhat sort of background is typical for someone picking up clojure?
21:06cemerickLau got me thinking about throwing together a proper clojurebot.
21:06wastreli know a lisp bot
21:07cemerickyeah, there's a lot of them out there. I think using a bot programmed in a different language is a cop-out. :-)
21:07Chousercemerick: sure -- I'd be happy to hook the existing HTML generation into a bot instead.
21:07cemerickThere's sure to be a java irc lib floating around, so it shouldn't be too hard.
21:08cemerickChouser: We'll see what happens. I'm on deadline for next Friday, so it won't happen until after then.
21:08Chouseryep
21:08cemericktoo many side projects, absolutely no time :-)
21:09Chouseryep :-)
21:09ChouserI started peeking at the Java security sandboxing stuff so we could allow the bot to eval expressions for us ... but then I got distracted by something else.
22:40jgrantis there an easy way to convert an IPersistentList to a java Array ?
22:48Chouseryes
22:50Chouserto-array to get an array of Object
22:50Chouserinto-array to get a more specific type of array
22:54jgrantthanks !