#clojure logs

2008-06-15

08:48rhickeyChouser: a var root is an atomic thing (a single Object). For last-one-in wins, write-over behavior nothing special is needed, other than that it be volatile, which it is.
08:49rhickeyread-modify-write is trickier, and that is why there are synchronized bindroot and commuteRoot, which you can use for safe ongoing multithreaded mutation of a var root (not recommended generally)
09:05meredyddWhat's the idiomatic way of dealing with Maps and Lists passed to you by Java code?
09:07meredyddA lot of the syntactic pretty (using assoc to add elements, invoking a map to look up elements) is understandably dependent on the object being a Cojure persistent map
09:08meredyddBut is there a sensible way to do a conversion?
09:10meredydd(sorry, I mean "idiomatic" rather than "sensible". I'm aware it's possible to (reduce) across (keys x), using (assoc)...)
10:02rhickeymeredydd: (into {} your-java-map)
10:03meredyddaha. Thankee, rhickey.
10:05rhickeybut note that seq/get/contains?/count/destructuring etc work for Java maps without conversion
10:55meredyddrhickey: Is the website your patch? If so, the redirects from sourceforge.net are all broken after the redesign (which is a pain, as they tend to score high on google)
13:03jcrites_how does one partially apply a function in closure?
13:03jcrites_say I want a function that adds one to a number
13:03jcrites_I want something like ....(+ 1)
13:03jcrites_do I need to use the fn form?
13:03jcrites_or is it possible to partially apply functions to other functions?
13:03jcrites_(or values)
13:06jcrites_ah, I see..... (partial + 1) is the same as (fn [x] (+ 1 x))
13:09hoeckjcrites: you can use #(+ 1 %) too
13:09jcrites_oh? cool, thanks!
13:09jcrites_what form is that exactly?
13:10hoeckjcrites: its an abbrevation for fn
13:12hoeck#(...) => (fn [args] (...))
13:15jcritesis % special then?
13:16Chouserjcrites: inside #(), yes it is. Also: %, %1, %2, etc. and %&
13:16Chouseroh, % is apparently special all the time -- only allowed inside #()
13:18jcrites% vs %&?
13:19jcritesis %& "rest"?
13:19Chouseryup
13:19jcriteslike %1 %&
13:19jcritesah, k
13:19Chouseryup
13:19jcritesClojure is pretty neat
13:19Chouseryup
13:19jcrites:)
13:19Chouserthat little feature is documented at http://clojure.org/reader?responseToken=e9a25f9ed12c8e758646d63847044670 -- search for Anonymous
13:29jcriteshmmm
13:29jcriteshow come closure lists print with ( ) ?
13:30Chouserclassic lisp. what should it use?
13:30jcritesthis confuses me
13:30jcrites> [1 2 3]
13:30jcrites[1 2 3]
13:30jcrites> (rest [1 2 3 4])
13:30jcrites(2 3 4)
13:31jcritesis it something to do with sequences?
13:31jcritesI would expect (rest [1 2 3 4]) to print [2 3 4] not (2 3 4)
13:31jcritesmaybe there is some subtle property I am missing
13:31Chouserthat's a great question.
13:32ChouserI think sequences also print with ()
13:32jcrites> (list 1 2 3)
13:32jcrites(1 2 3)
13:32jcritesso I guess [] is just for vectors
13:32Chouser(seq [1 2 3]) ==> (1 2 3)
13:32Chouseryes, [] is just for vectors
13:33Chouser"rest" and "first" work on sequences, and "rest" always returns a sequence.
13:33jcritescan a sequence also be a vector?
13:34jcritesi.e., let's say I do (rest [1 2 3]) and I get a sequence. can I get back to the vector 'type' and index on it?
13:34jcritesor is it hidden by the rest?
13:35jcriteswell let me find out :)
13:35jcritesbut even if it works it doesn't mean it's guaranteed to work right?
13:35Chousersorry, watching kids too here...
13:35Chouserseqs are generally views of the underlying object
13:35ChouserI don't think you can get back to the underlying object from a seq
13:36Chouserbut you can choose to use more specific functions.
13:36Chouserfor example, cons works on seqs and returns a seq: (cons 4 [1 2 3]) => (4 1 2 3)
13:37jcriteshow can cons take [ ]? is it treated like (cons 4 (seq [1 2 3])) ?
13:38Chouserexactly
13:38Chouserall the seq-based functions wrap a (seq ...) call around their collection parameter
13:38Chouseron the other hand, conj is implemented for each of the collections seperately
13:39Chouser(conj [1 2 3] 4) ==> [1 2 3 4]
13:39Chouser(conj {1 "one", 2 "two"} [3 "three"]) ==> {1 "one", 2 "two", 3 "three"}
13:48meredyddAnyone care to enlighten me as to why macros can be passed as function parameters without the compiler squawking?
13:49meredyddI just attempted to use (and) in a (reduce), and got a nasty surprise
13:49meredyddIt executed just fine, but using the macro's code-transformation function
13:51meredyddso, if I try: (def a true) (def b false) (def c true)
13:52meredyddand then (reduce and [a b c])
13:52meredyddI get: (clojure/let [and__152 (clojure/let [and__152 true] (if and__152 (clojure/and false) and__152))] (if and__152 (clojure/and true) and__152))
13:52meredydd(where I'd normally be expecting 'false')
13:53meredyddI appreciate that it might be a good idea to *allow* people to access a macro as a function...but does it have to happen silently like that?
13:54hoeckmeredydd: macros are functions that are handled specially by the compiler
13:55hoeckand its bad the compiler doesn't warn if you pass them around as functions
13:55meredyddI agree with you on both counts.
13:56meredydd(although there should be a way to pass a macro as a funarg if you assure the compiler you know what you're doing)
13:58hoecki guess macros being functions is an implementation detail for clojure
14:02jcriteswhat's the idiomatic way to represent factorial recursively in Clojure?
14:05jcrites(defn fact [n] (* n (if (> n 1) (fact (- n 1)) 1))) is that good?
14:06jcritesI'm new to Lisp
14:12Chousermeredydd: you can't pass macros where something (like reduce) is expecting a function.
14:12hoeckjcrites: totally valid
14:12ChouserBut frequently trivial to wrap a macro call in a function: (reduce #(and %1 %2) [a b c])
14:12meredyddChouser: On the contrary; I have just spent some time learning that you *can*...
14:12jcritesso I'm writing this function and I need a way for it to call itself
14:12Chousermeredydd: sorry. "shouldn't"
14:12jcritesdoes that mean I need to put it inside a let expression?
14:13meredyddChouser: Yeah. I'm taking that as a given ;)
14:13jcritesand can functions in let expressions call themselves through their name in the let expression?
14:15Chouserfunctions defined using defn can call themselves by name (although depending on how you use it you might overflow Java's call stack)
14:15Chouserfunctions defined using fn can be named for the purpose of calling themselves: (fn foo [x] (...can call foo here...))
14:16jcritesoh
14:16jcritesI didn't know fn worked that way
14:16jcritesI was trying to do some awkward (let [f (fn ...)]) thing :)
14:16Chouseryeah, there's an optional name parameter there. But that name is not valid outside the (fn) expression
14:16jcritesthat's awesome!
14:16jcritesit makes sense
14:17hoeckthis name us also useful for debugging
14:17Chouseryou still might need to do that if you want a function defined locally: (let [f (fn ...)] (f ...))
14:17hoeckit pops up in the stack if the named fn throws an exception
14:17Chouseror worse if f is recursive: (let [f (fn f [] ...)] (f ...))
14:18Chouser...because in that case it's really two different f's: (let [f1 (fn f2 [] ...use f2 here...)] (f1 ...))
14:19jcritesright. I was just trying to use let to give it a name for itself :) but if it has that built-into fn already I'm set.
14:19jcritesso does Clojure have something like "letrec"?
14:19jcritesor is that just "def"
14:19Chouserbut you should be a bit careful doing recursive calls like that, since there's no tail-call optimization. You may have to use recur or lazy calls.
14:19jcritesoic
14:20jcriteslet me show you what I'm actually trying to do.... 1 sec
14:20Chouserjcrites: I don't think there's a letrec. I suppose you could write a macro that does (let [f (fn f [] ...)] ...) for you.
14:21Chouserdef and defn are always for globals
14:36jcriteslet's say I have a list.... I have a value... if the value is non-nil, I want to append it; if it's nil I want to do nothing
14:36jcritesshould I write that logic myself or is there a built-in with that kind of meaning?
14:36Chouservector, right? you don't append to lists generally.
14:37jcritesI was planning on building up a list with recursive calls and cons ....
14:37Chouserjcrites: out of curiosity, what's your language background?
14:37jcritesI'm trying to translate my little Scheme knowledge to Clojure :)
14:37Chousercons "appends" at the beginning. Which is fine if that's what you want.
14:37jcritesmost of my experience is with curly-bracket languages. I have some university experience in Scheme
14:38Chouserok, very good. So you already know what I just said about lists. :-)
14:38Chouserok, so cons-ing onto a list returns a new list, right? It never modifies anything. So by "do nothing" you mean return the original list unchanged?
14:38jcritesright
14:39yrbjcrites: Just out of interest what is your opinion on all the parens... ;)
14:39jcritesI'm trying to write some parser combinators in Clojure as an exercise, if you know what those are :)
14:39jcritesI don't like them :)
14:39ChouserI've heard them mentioned, but the words me nothing to me. :-)
14:39jcritesso a parser is a function that takes in a sequence and returns a sequence (possibly empty)
14:40jcritesit returns what it has "matched"
14:40jcritesso if your parser matches everything up to space " " it might take in the sequence "hello, world" and return the sequence "hello,"
14:40jcritesa parser combinator is a function that builds parsers
14:40Chouserafaik, the only builtin thing that does a bit like you want is concat (or mapcat): (concat nil '(1 2 3)) ==> (1 2 3)
14:41jcritesyou could imagine a parser combinator const that takes in a string. what it does is build a parser that matches exactly that string or fails (matches nothing, returns nil)
14:41Chouserbut of course that only works if the first thing is a seq of some sort, not a single value.
14:41jcritesso I was trying to write some of these combinators in Clojure...
14:42jcrites(defn success [] (fn [q] (list q)))
14:42Chouseryeah, ok, sounds like a good exercise.
14:42jcrites(defn failure [] (fn [q] nil))
14:42ChouserHm, I may have read about those in On Lisp.
14:43ChouserI don't think clojure has a builtin that does: (if x (cons x lst) lst)
14:43jcritesyeah I am just trying to make sure I'm doing things the right way :-)
14:43Chouseryup. nobel. :-)
14:43jcritessometimes the urge to make something yourself is actually misdirected and only exists because you're doing something wrong, hehe
14:44jcritesI've been bit by that before ;-)
14:44Chouseryep, especially in schemes and lisps.
14:45jcriteslooks like cons may already do what I want kinda...
14:46Chouserreally? maybe I misunderstood your question.
14:46jcriteshere is my attempt at const:
14:46jcrites(defn const [cq] (fn p [q] (if (= (first cq) (first q)) (cons (first cq) (p (rest q))) nil))
14:46jcriteson entering it the repl is frozen so I must be doing something wrong :(
14:47Chousermissing a )
14:47jcritesbasically, if the first of cq and q are equal then return a new list with that element, recur by calling myself
14:47jcritesotherwise return nil (empty)
14:47jcritesit looks like cons will, ... well, (cons 1 nil) => (1)
14:48jcritesif I remember Scheme that's the same way Scheme does it
14:48Chouserah! yes, but (cons nil '(1)) => (nil 1)
14:48ChouserI had your question backwards.
14:48Chousernil acts like an empty list for cons
14:48jcritesI actually think I was thinking the wrong way ;-) or not explaining that the "rest" would be returned by a recursion
14:49jcritesso you mentioned that this will be real Java recursion and that that could bite me
14:49Chouseryes
14:49jcritesif I were going to use a bunch of these all nested together, on files with thousands of characters, I'm guessing I would be screwed?
14:49Chouserbut just replace (p (rest q)) with (recur (rest q)) and you're all set
14:49Chouser...and then you don't need to name p anymore
14:50jcritesoh, so recur is always a call to the self-fn ?
14:50jcritesthat's neat
14:50Chouseryes
14:50Chouseror the surrounding (loop ...), whichever is closer.
14:50ChouserIt's a pretty elegant hack to work around the JVM's lack of tail call optimization.
14:51jcritesinteresting
14:51jcriteshow does it work? I mean Clojure is compiled to jvm bytecode right?
14:51Chouseryes
14:52ChouserI don't really understand it that deeply, but I think regular clojure functions calls (including recursive ones) are compiled into real Java function calls.
14:52Chouserhense the stack overflow issue.
14:52jcritesyeah, it's complex... I wonder how the compiler avoids that with recur
14:53ChouserBut recur is a special form that the Clojure compiler must turn into some kind of goto (with parameter munging).
14:53jcritesmaybe it actually compiles to a JVM loop.... yeah, that makes sense
14:55jcriteslink?
14:55jcritesI'm no JVM expert but I've worked on compilers before
14:55Chouserum, sure, just a sec...
14:55jcritesoh no
14:55jcritesI got an error
14:55jcritesjava.lang.UnsupportedOperationException: Can only recur from tail position
14:55jcrites(defn const [cq] (fn [q] (if (= (first cq) (first q)) (cons (first cq) (recur (rest q))) nil)))
14:55jcritesit isn't in the tail position I guess :(
14:55Chouseroh! I'm sorry, I misread your code.
14:56Chouserno, of course the compiler's right -- you're passing the result of recur to cons
14:56Chouserso even real tail call optimization wouldn't help you.
14:58jcritesso I can't implement my algorithm recursively with ease
14:58jcritesperhaps I can get the cons inside the recursive call....
14:58Chouserwell, you can as long as it's not too deep. :-)
14:59Chouserright, that's one route -- rewrite your function to be tail-call-optimizable (something else I read about in On Lisp)
14:59Chouseryou actually might be able to make it lazy instead.
14:59Chouserreplace cons with lazy-cons
15:00Chouser(...and put p back in)
15:00meredyddMore questions: Why does (contains?) only work with maps, and what should I be using instead on (seq)s?
15:01Chousercontains? is constant time (or log(n)) on maps. for a seq you have to search linearly
15:01Chouser(some #{3} [1 2 3 4])
15:01meredyddAnd there are two separate functions just to keep me from accidentally doing so?)
15:02meredyddAha. Ta.
15:02jcritesit sort of makes sense
15:02Chousersome is much more general than just finding a specific item in a seq
15:02jcritescontains? reads like a quick predicate, not a "search"
15:03meredyddChouser: Yep, that's what I was planning on doing; just reckoned it ugly. However, the neat trick with the invokable set renders it quite passable :)
15:04Chousermeredydd: yeah. That's straight from rhickey -- I didn't think of it.
15:05meredyddOh, plus, looks like (contains?) works exactly as advertised on vectors - if you feed it an index, it will tell you whether (get) will give you anything
15:05Chouserhuh!
15:05meredyddSo, it fulfils exactly the same contract. Clever.
15:06meredyddI do love this language. Whenever I poke into "this looks like a pain", the answer regularly comes back as "oh, that *is* clever"
15:06jcrites:)
15:06jcritesit has a lot of elegance and aesthetic appeal
15:07jcritesand consistency to the design of the APIs
15:07ChouserI would give up some performance for those characteristics, but apparently I don't have to.
15:08yrb:P the cooler bit is that you can invent the language you need as you need it
15:08jcritesso Clojure manages not to work with boxes objects often?
15:09meredyddThat's the wonder of Lisp, yrb. The wonder of Clojure is that so far, I'm spending my time doing that for my own purposes, rather than trying to work around corner cases in the language design (*cough* Common Lisp *cough*)
15:10jcritesCommon Lisp has some ugly parts of the language that I sort of don't understand
15:10jcriteslike the fact that functions aren't values, and you need to do weird #' stuff
15:10meredyddjcrites: I believe rhickey's been adding some explicit native arithmetic. But normally, I think most stuff is boxed right now.
15:10meredyddjcrites: That's to do with being a Lisp[2], rather than a Lisp[1] which is what Clojure is.
15:10jcriteshow does it perform well then? :/ I would expect boxing and unboxing between every operation would be fairly expensive
15:11jcritesmeredydd: what does that mean? [1] and [2] ?
15:11meredyddjcrites: For numerical applications, sure. However, most people don't write numberical apps
15:11fyuryuand you can put type hints for the compiler
15:12jcritesbut any application works with integers quite a lot.... iterators in a loop for instance
15:12meredyddIf you're using iterators in Clojure, it's almost always implicitly, as a (for) or a (seq) - and the iterators themselves are written in Java using native arithmetic
15:13jcritesahh
15:13meredyddAs for the Lisp-1/Lisp-2 distinction, check out http://www.nhplace.com/kent/Papers/Technical-Issues.html
15:14meredyddBasically, Lisp-1s (like Clojure, and I believe Scheme) have functions in the same namespace, whereas Lisp-2s (like Common Lisp) have separate namespaces for functions and everything else
15:14yrbI think that would be a micro optomisation also, any who knows what the jvm is doing...
15:14yrbanyway*
15:15yrbthere is some really awesome tech in the jit
15:15jcritestrue
15:15jcritesI suppose it would detect if the boxed numbers are not being used as objects
15:16yrbI think it will even generate fall through code for "hot" code paths
15:17jcritesfall through code?
15:17jcrites(I don't know a ton about optimization)
15:18meredyddOh, yuck. Chouser, any idea how to do backquote-style quoting explicitly (ie like (quote))
15:20yrblike say you call a function with 4 params and it ends up at run time 9 times out of 10 3 params are the same it can gen native code specifcally for that case
15:21yrbbut it isn't iimited to functions it is entire code paths i think
15:26yrbDoes anyone know of any lispy gui toolkits that have a good design, I am tring to write a Swing wrapper (called Cling ;) ) just looking for a good design to get inspired
15:27jcriteswell, you might take lessons from the declarative interface kits
15:27jcritesin Yegge's recent post he talks about ...
15:27jcritesin Rhinos and Tigers: http://steve-yegge.blogspot.com/2008/06/rhinos-and-tigers.html
15:28yrbThat is the idea, I have been having a look at jelly swing
15:28jcritesah, ok :)
15:35ChouserI believe (almost?) all clojure functions take boxed args and return boxed, so no boxing/unboxing.
15:36jcriteswell, I mean if you have (+ n 1) there has to be some boxing
15:36Chouser...except when calling Java functions, or the new native number stuff, in which case it tries to use native... somehow...
15:40Chouserjcrites: numeric literals are boxed, probably at compile time
15:44Chousermeredydd: I don't quite understand. you want ' is to quote as ` is to _x_ ?
15:45meredyddYep.
15:45Chouserhm, I don't think there is such a beast.
15:45meredyddLooking at what macroexpand-1 says, it's all done with a very hairy hack inside the reader
15:45Chouseryeah, sound about right
15:46meredyddso I just rewrote the macro to require the user to backquote the args that go in.
15:46meredyddLess pretty, but livable.
15:46Chouserhttp://n01se.net/paste/Yiq -- Clojure Compiler.java RecrExpr.emit
15:47Chouser...that appears to emit a bunch of local variable manipulation followed by a goto.
15:53jcriteshmmm
15:53jcrites(first nil) => nil ?
15:54yrbmeredydd: Just out of interest why did you need it `ed
15:54meredyddyrb: I'm writing a unit test framework.
15:55jcritesbah
15:55meredyddThere's a utility macro for asserting that a macro expands as it should
15:55jcritesI found the bug
15:55jcritescan you spot what's wrong with this function:
15:55meredydd(defmacro assert-macro [macro-expr expanded-expr]
15:55meredydd `(assert (= (macroexpand-1 ~macro-expr) ~expanded-expr)))
15:55jcriteshttp://pastebin.com/d45395948
15:56jcritestricky bug I wrote there :-)
15:56meredyddyrb: Ideally, I'd like to do something like ~(quote expanded-expr) and ~(quote macro-expr)
15:56jcritescauses infinite recursion
15:57jcriteswhat's the problem? the problem is that if both sequences are nil, (first nil) returns nil, meaning my function recurses infinitely :(
16:00jcritesnow I'm getting wrong number of arguments passed to cons... sheesh... I suck >_<
16:00jcritesis 2 not enough?!! IS IT NOT ENOUGH?!
16:09jcriteshmmmm
16:09jcriteswhat have I done wrong with this function?
16:09jcrites(defn const-p [cq q] (if (and cq (first-eq cq q)) (cons (first cq) (const-p (rest cq) (rest q)) nil)))
16:09jcritesI keep getting the error: wrong number of args passed to cons
16:10Chouser(cons (first...) (const-p...) nil)
16:11jcritesoh I thought I put the nil on the if
16:11jcritesdid I really put it in the cons >_<
16:11jcritesoh shit you're right. thanks!
16:11Chouserthis is why you need an editor to help
16:11jcritesI agree :(
16:11Chouserwhat are you using?
16:13jcritesnothing
16:13jcritesworking on the REPL
16:15yrbemacs works really well with lisps
16:35yrbmeredydd: does this not work? (defmacro macro-equal [me ee] `(assert (= (macroexpand-1 (quote ~me)) (quote ~ee))))
16:36meredyddyrb: Nope. The macroexpand will add namespace qualifications to symboles, whereas (quote) won't.
16:36meredydd*symbols
16:38yrbah I was assuming the user must also add namespace quals
16:41meredydd...which is what I've fallen back to. But it's a PITA.
16:41yrbhow about doing this? (defmacro macro-equal [me ee] `(assert (= (macroexpand (quote ~me)) (macroexpand (quote ~ee)))))
16:42meredyddOoh, goodness. That's going to look ugly as sin on the debugging, but should cure the other stuff...
16:42meredyddGood idea.
16:43meredydd(The wheels on the bus go "hack hack hack"...)
16:44yrbthat so isn't a hack... rofl... well not when you look back at java/c++ land
16:46yrbbut your right will look ugly as sin debugging
17:14meredydd*blinks
17:25jcriteshmmmm
17:25jcritesI love how easy it is to represent algorithms in Lisp
17:26jcritesnone of the... what he called "ceremony" :)
17:26jcritesbut it's hard to figure out the right way to do it some of the time
17:26jcriteswhat I'm trying to do (recursively, which is perhaps why it's hard) is check if one sequence is a sub-sequence of another sequence. if so, return it, otherwise return nil
17:27jcriteswithout writing too many conditionals...
17:28jcritesI feel like there should be some elegant way to do this without the "if this - if that "
17:28meredyddyrb: If you're interested, it turns out that even that wasn't quite right.
17:28jcritesmy algorithm now is basically..... if (both sequences are nil) true (else .....
17:29jcriteselse if the first element of each sequence is equal, then call self recursively, otherwise return nil
17:29yrbmeredydd: Interesting, where does it fail?
17:29meredyddBecause the args still had non-namespaced stuff, which (macroexpand) wouldn't touch
17:30meredyddand apparently, nested `s don't work, so you can't invoke that quote on it inside the macro
17:32meredyddIt could probably be done with a macroexpand, followed by a code-walk to force the namespace qualifiers onto any unqualified symbols
17:32meredydd(that is, "a full macroexpand, like you were suggesting, followed by a code-walk...")
17:33meredyddBut I think it's just passed my "can-be-arsed" threshold, and I'm just mandating backquotes on args passed in :P
17:34yrbI agree :)
18:03pjb3When I am in the Clojure REPL
18:03pjb3If I define a function that will run a function in a thread
18:03pjb3like this:
18:03pjb3(defn in-thread [f] (.start (new Thread f)))
18:04pjb3When I call it like this:
18:04pjb3(in-thread #(prn "hi"))
18:04pjb3it doens't print
18:04pjb3but as soon as I evaluate something else
18:04pjb3like:
18:04pjb3()
18:04pjb3Then I see the output
18:05pjb3Any idea as to what causes that?
18:07JamesIrypjb3: just a guess, but could it be that the repl is buffering stdio so that output in other threads doesn't appear randomly as you are typing.
18:08pjb3yeah, I suppose that makes sense
18:08JamesIryShould be easy to test...make in-thread do something with file IO instead of stdio
18:09JamesIryThen go look at the file in a separate window
18:13Chouseralways use (flush) after any debugging (prn)s
19:54jcriteshey, anyone know how I can convert a sequence to a vector?
19:54rhickey(vec your-seq)
19:55jcrites(vector my-seq) ?
19:55jcritesI tried:
19:55rhickeyno, vec
19:55jcritesmy version doesn't have that. is that new?
19:55rhickeyIn the latest release 20080612
19:56jcriteslet me get that one. thanks :)
19:56rhickeyif you are on an old release, use (apply vector your-seq)
19:56jcriteshmmm
19:56jcriteshow can (f x) be different from (apply f x) ?
19:57jcritesor is it just with values of f which are special forms?
19:57rhickeythe last arg to apply must be a seq, which gets 'unrolled' into args
19:57rhickey(apply + [1 2 3])
19:57jcritesahhhh, that makes sense then
19:58jcritesthank you
19:58rhickeyvs (+ [1 2 3])
19:58rhickeynp
20:06jcriteswhat would be the right way to append an element to the end of a sequence? (returning a new sequence)
20:07jcritesI guess I am too dumb to do it recursively with recur :)
20:08jcriteshmmm maybe I can do it!
20:08jcritesoh, nope, I can't...
20:09jcritesi'm not sure it's possible to do tail recursively because the cons can't be called until after the recursion returns
20:09jcritesmaybe you could do it somehow by reversing the sequence?
20:09slavajcrites: (reverse (cons elt (reverse seq)))
20:10jcriteswould you consider that better or worse than converting it to a vector, vector-appending, and converting to seq? (the last step which may not be much of a "conversion")
20:10slavawell, the underlying issue here is that you're peforming an operation which the data structure is unsuited for
20:10slavaif you need to do this once or twice, no big deal, if you're doing it in a loop consider just using a vector
20:11jcritesI guess you're right. I'm thinking in the non-functional mindset
20:11jcritesI'm trying to implement Sparsec/Parsec combinators in Clojure as an exercise actually :-) the DS I'm discussing will actually be my "parse string"
20:11jcritesI was hoping to get around Sparsec's ParseResult and have parsers return nil on no-match
20:12jcritesthen that means I need an end-of-string symbol
20:12jcritesso this question came up from how can I append :eof to (seq "some string to parse")
20:12jcritesand it looks like I should just do something like (defn append [v e] (assoc v (count v) e))
20:12meredyddrhickey: The sourceforge redirects are broken from what I can see.
20:13jcritesthey are totally broken :/
20:13jcritesAFAICT a "/" got lost somewhere
20:13slavai don't think parsec needs to be implemented with an 'end of string' symbol
20:14jcritesno, it doesn't need to be, I just think it would make it more elegant
20:14slavathe way its usually done is with a list monad
20:14jcritesI mean in Scala for example, oh, I guess you aren't necessarily familiar with that, I forgot
20:14slavai am, somewhat
20:14jcritesScala has an implementation called Sparsec, with a structure called ParseResult which is a boolean and some stuff
20:14jcritesI was hoping for my Lisp (Clojure) version to return nil/non-nil
20:15slavain haskell's parsec, parsing yields a lazy list of results
20:15jcritesthe problem with that is, what if your parser matches the whole sequence?
20:15slavawhere a parse result is some object (otuput by action parsers) together with the rest of the sequence
20:15jcritesthe "rest of the sequence" is nil, then, which is the same as the error case
20:15jcritessince they need to be distinguishable, I was considering adding a magic character on the end, designated :eof
20:15jcritesin that way, all parsers will always terminate with "next" pointing to the eof character
20:16slavathe error case outputs an empty list of parse results
20:16slavanot a list containing nil
20:16jcritesso parsers cannot match nothing?
20:16jcriteshow do you match * ?
20:16slavathey can, in which case you get a list contianing nil as the parse result
20:16jcritesah, interesting
20:17jcritesI hadn't thought of returning a list of parse results at all :) although I was pondering how to implement a "fork" combinator
20:17slavathat's how parser combinators work
20:17slavaeach parser returns a list of parse results
20:17jcritesit makes sense why the list monad then
20:17slavaparser concatenation is 'append' lifted to hte list monad
20:17jcritesaren't most of the parsers deterministic though? why do they need to return a list of results?
20:18jcritesthe only reason I had considered a list of results was for a nondeterministic "fork" operator
20:18jcritesoh, is the list of results items matched in series?
20:18slavawell, consider a parser like (a*)(a*)
20:18jcritesI was thinking of them as alternatives up until now
20:18slavaif you parse aaaa, there are 4 possible results
20:18rhickeymeredydd: clojure.sourceforge.net goes to clojure.org, no?
20:18slavaa|aaa aa|aa aaa|a
20:18jcritesrhickey: it's broken somehow, try some google searches
20:18slavaoh, and |aaaa aaaa|
20:19jcritesso all of those are returned.... oic ...
20:19slava( scratchpad ) "aaaa" "a" token <*> parse llength .
20:19slava5
20:19slavathere are 5 results there, "", "a", "aa", etc
20:19slavabut if we insist that it must patch the whole string
20:19slava( scratchpad ) "aaaa" "a" token <*> just parse llength .
20:19slava1
20:19slava'just' filters out all parse results where the 'rest' is not nil
20:20slavaparser combinators are all operations on lazy lists
20:20jcritesinteresting.... the Scala versions are a deterministic version of that, I guess
20:20slavathen they're not parser combinators
20:20jcritesI am not very familiar with the original Parsec
20:20slavathey're parsing expression grammars
20:20slavapegs don't backtrack after a successful match
20:21slavaso you either get one parse result or nothing at all
20:21jcritesyes, that's how they are
20:21slavaso for example, with pegs, (a*)a will never match aa
20:21slavabecause (a*) 'eats up' the aa
20:21slavaand it doesn't backtrack after that, ever
20:21jcriteshmmm
20:21slavawith parsec, it fails to match the second 'a', and backtracks, then suceeds
20:21meredyddrhickey: Yes, but the sub-URL forwarding is borked.
20:21meredyddrhickey: http://clojure.sourceforge.net/reference/namespaces.html (just to pick one out of the air)
20:21jcritesthanks for the insight; I think you have saved me from some hours of bad design decisions ;-)
20:21slavajcrites: parsec parsers are generally less efficient
20:22slavabut they're more expressive
20:22rhickeysome of them will be as they use subdirectories which my new wiki doesn't support
20:22slavahowever i'm pretty sure anything you can parse with parsec can be parsed with peg, except sometimes the peg grammar can get convoluted
20:22jcritesI was actually thinking of trying to generate a greedy | non-greedy fixed implementation from the combinators or something
20:22slavaregular expressions are very hard to embed in peg for example
20:22jcritesyou build up a combinator expression then serialize it, compile it, or something
20:22slavayes, in peg, everything is 'greedy' unless you add a lot of negations
20:23slavain factor, we have parsec parsers, which are built with lazy lists and are inefficient, and peg parsers which compile to machine code but are a bit harder to use
20:23jcritesis that generally a problem if you were going to, oh, say totally hypothetically ;-) use it to parse a simple language?
20:23meredyddrhickey: I don't think they even get that far - I'm getting "Unknown host 'clojure.orgreference'"
20:23meredyddrhickey: Looks like you're missing a '/;
20:23meredydd*'/'
20:23jcritesmeredydd: it's missing a /
20:23slavajcrites: well, pegs can be used to parse very complex languages
20:23slavalike javascript
20:23rhickeyjcrites: ah, just a sec
20:24jcritesoic, a peg is what I want then :)
20:24slavaand they're more efficient than parsec
20:24jcriteshowever, I was trying to add something quite odd
20:24jcritesI was trying to add a "fork" peginator
20:24jcrites(peginator, combinator)
20:24slavai'm not familiar with that type of thing
20:24slavai didn't implement any of this stuff, i just use it a fair bit
20:24jcriteswhich would parse (depending on how I decide to do it) either both arguments, or one
20:24jcrites(or fail and nothing)
20:25jcritesthere are some options for the specifics, but the general idea is to parse both alternatives, sort of like a real parser combinator
20:25jcritesexcept both are valid syntactically
20:25rhickeyjcrites: ok, you'll get a 404 now
20:25jcritesthe two valid syntax trees would be the result of the parsing operation
20:25jcritesthe goal of this being to allow simple ambiguous grammar in a programming language
20:26jcritesI know it's crazy; I just want to try it ;-)
20:26jcritesone could imagine a Lisp where either (a + b) or (+ a b) are accepted
20:26jcritesyou could, if you wanted, represent both expressions by the same AST
20:26slavawhat if a evaluates to a function?
20:26jcritesor they could be different
20:26jcritesif there is semantic ambiguity it would be an error
20:26slavaat run time?
20:27slavasorry, i don't like ambiguity in syntax :)
20:27jcritesI don't know as much about dynamic languages :) I was thinking of this from a static perspective
20:27jcritesoh I know, it might be a horrible idea
20:27jcritesimagine reading (f g h)
20:27jcriteswho knows what that means
20:27jcritesg(f,h)?
20:27jcritesf(g,h)?
20:27jcritesor maybe it even means h(f,g)! :)
20:28jcritesI wasn't expecting this particular example to be a good use of ambiguous syntax
20:28jcritesI was thinking maybe about using [ ] to specify a variety of similar data structures or whatnot
20:28jcritesthe two alternative ASTs might be very different and easily distinguishable by the programmer
20:28jcritesthat would be a better usage of ambiguous syntax
20:29jcritesmy goal was just to not not have that option because of technical limitations in the parser ;-) I think I can write semantic analysis algorithms to handle it intelligently if I can support nondeterministic parse trees
20:29jcritesbut only experimenting with the language feature will tell me for sure if it's a horrible idea, or one worth investigating ;-)
20:30jcriteswhere the program expression mirrors a very numerical form, I might think of it as OK.... (a+b)*(c+d) or something
20:30jcrites(in an otherwise Lisp program)
20:30jcritesI'm also going to try ambiguous syntax by omission
20:31jcritesyou could imagine a Lisp program where many parentheses are simply omitted, inferred by other means such as indentation or newlines
20:31jcritesin that case, one way I could implement it is via ambiguous syntax... I'm not sure exactly how to do that without an explosion of AST cases, but the general idea makes sense I think
20:40rhickeymeredydd: links better now?
20:40meredyddMuch, thanks :)
20:40rhickeythanks for the heads up
20:50drewrIs trunk@906 broken?
20:51drewrException in thread "main" java.lang.NoSuchMethodError: clojure.lang.RT.cons(Ljava/lang/Object;Lclojure/lang/ISeq;)Lclojure/lang/ISeq;
20:55slavado you guys have continuous integration?
20:56drewrWe need to get rhickey on a dvcs.
20:57drewrThese "interim checkins" are unnecessary.
20:57meredydddrewr: I dunno...I like being able to experience something he's playing with
20:57meredyddThe real problem is that svn sucks at branch-and-merge
20:57jcritesa dvcs is not necessary for development commits to be made on a development branch ... eh ?
20:57drewrYou'd still get the history. He could just push it when it's functional.
20:57jcriteswhat sucks about it?
20:58slavawe have a bunch of machines which compile and run tests, if everything passes they tag the revision they just built
20:58dudleyfjcrites: Merging is way harder than it needs to be
20:58slavaso people can check in broken stuff and users can just pull the 'clean' tag
20:58slavawhen you support 11 architectures its unrealistic to expect every developer to test on every platform before checking in changes
20:58jcriteswhat's harder about it? forgive me, I haven't used svn very often in any kind of production environment
21:00meredyddjcrites: You need to specify revisions manually. You can't do sensible things like "merge all changes made to meredydds-devel since it diverged from trunk"
21:00dudleyfsvn makes you track down the branch point manually, then apply a diff, then fix any conflicts, then commit
21:00jcritesoh, so effectively you can only merge revisions, and not branches?
21:00jcritesthat does sound painful.
21:00meredyddjcrites: What you do, effectively, is get a diff between two revisions on one branch, and apply it to another.
21:01meredyddSo, I'd take the diffs between 503 (when I branched) and now, on my branch, and apply it to trunk. That sort of thing.
21:01jcritesI wonder what their rationale is for that kind of merge behavior .... ?
21:03meredydd"It's one hell of a lot better than CVS" :D
21:03meredyddI don't think it's that bad, actually - if there were sensible automated ways of figuring out which revisions you wanted to take, I think I'd be quite happy with it
21:04jcriteswhat happens if you specify an earlier revision? does the merge get all messed up, or just fail?
21:04jcrites(a revision already merged, say)
21:04meredyddUhh...it tries as best it can.
21:05meredyddIt's exactly like applying a patch from the wrong revision
21:09drewrAh, 906 isn't broken. I had to ant clean myself of the cruft of many revisions ago.
21:18slavaant still doesn't support automatic recompilation of dependencies? :)
21:58pjb3I'm working on adapting the server socket repl example from the wiki into a toy web server
21:58pjb3http://pastie.org/215618
21:59pjb3Weirdest thing though, if I redefine the handle-request function and just change the value of msg
21:59pjb3the next request I make from a web browser uses the old version of msg
21:59pjb3but the 2nd request uses the updated version