#clojure log - Apr 26 2011

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

0:00 dnolen: mec: you could reify instances of ISeq to override the undesirable behavior.

0:04 mec: but I guess what you really want is custom seq behavior w/o losing the record benefits?

0:06 mec: dnolen: I thought you couldnt implement ISeq directly on a record, I swear before it was giving me errors, so my initial question is pointless

0:07 chrissbx: mec: well, how do I "go to the repl" when in slime? slime is opening the inspector at this point, no way to type anything

0:08 mec: chrissbx: hmm in mine it opens the inspector in a different buffer so I still have the repl open

0:09 chrissbx: yes -- aha I can still type in the repl window and it will evaluate even though it looks blocked

0:09 mec: chrissbx: yup, any locals in scope are available to play with

0:10 chrissbx: Hm, shows {} as local-bindings while there certainly are at least 2 function arguments where I put the break

0:10 Doesn't surprise me as the backtrace in slime also shows no locals everywhere.

0:10 Do I need to build the code with some debugging info flags?

0:11 s/everywhere/anywhere/

0:11 mec: chrissbx: ((fn [a b] (swank.core/break)) 1 2) gives me {a 1, b 2} with local-bindings

0:13 Ok so (defrecord Foo [bar baz] clojure.lang.Seqable (seq [_] '(1 2 3))) gives me a duplicate method error

0:13 chrissbx: hm, same thing for me; but when I do the same in code in a file, it eats some or all variables

0:14 like in (defn >>= [a b] (swank.core/break) ...) it shows {}

0:14 in (fn [state & cont] (swank.core/break), it shows cont but not state

0:14 dnolen: mec: yeah, you can't do that.

0:16 mec: dnolen: so I'd have to make it a type instead of record?

0:16 chrissbx: I guess it does some inlining and optimizes variables away.

0:16 Can I tell the compiler not to optimize?

0:24 dnolen: mec: I haven't found a good way to deal w/ that situation - in my case switching to deftype wasn't a big deal since I didn't need all the defrecord machinery anyway.

0:27 mec: a bit frustrating, but records have a contract to be map-like. Redefining seq like that would break anyone else trying to use your record in the usual way.

0:27 chrissbx: Am I too dumb to find the compiler options documentation, or are there no compiler options?

0:28 Are there declarations I can put into the source files?

0:28 tomoj: dnolen: whew, thanks

0:28 mec: dnolen: makes sense, I suppose deftype isnt so bad in my case anyway

0:28 tomoj: I don't like "it works (but it's evil)" advice

0:28 (I guess if it _really_ worked it wouldn't be evil)

0:29 mec: chrissbx: im not sure I've ever heard of any clojure compiler options

0:30 dnolen: chrissbx: optimizing variables way doesn't sound right to me.

0:31 chrissbx: well how else would you explain my observation?

0:35 dnolen: chrissbx: don't know, I'd bring it up on the ML, there's been a lot of chatter/interest in CDT

1:10 technomancy: no, it's known that the compiler clears locals when they aren't being used

1:10 it's called fine-grained locals-clearing, and it helps avoid head-holding in lazy seqs

1:10 you can only observe a local if you place the breakpoint between its introduction and its last use

1:11 see under The Gory Details: http://georgejahad.com/clojure/swank-cdt.html

1:11 chrissbx: ^^

1:13 mec: technomancy: thats pretty cool

1:14 technomancy: mec: yeah, it usually doesn't get in the way

1:16 brehaut: does slurp look in resources ?

1:21 technomancy: brehaut: (comp slurp clojure.java.io/resource)

1:21 brehaut: technomancy: great, thanks

1:22 technomancy: np

2:41 seancorfield__: gosh darn, they added more 4clojure problems!

2:46 amalloy: seancorfield__: lucky that we added an rss feed too

2:46 and registered 4clojure.org for poor misguided souls

2:49 seancorfield__: re: .org - excellent!

2:51 i haven't checked if the RSS feed is fixed on rockmelt yet

2:52 amalloy: seancorfield__: it's not fixed on chrome, so probably not. i'm told that if you install google's official "make rss feeds work" plugin, it works

2:56 seancorfield__: 'k... probably explains why RockMelt on the iPhone rejects the 4clojure.com/problems/rss link

3:03 brehaut: $findfn (fn [x] (= x 2)) [1 2 3 2 2] 3

3:03 sexpbot: []

3:05 amalloy: brehaut: there are so many goals for which 3 would be the right answer there...are you looking for (comp count fiilter)?

3:05 clojurebot: count arities is http://groups.google.com/group/clojure/msg/fb9930ba2a25d2dd

3:05 brehaut: amalloy: yeah i am

3:06 amalloy: in particular i was looking for not manual

3:06 amalloy: i thought you might want ##([1 2 3 2 2] 2) ; 3

3:06 sexpbot: ⟹ 3

3:07 brehaut: aha

3:07 i wish :)

3:09 markoman: hmh. im doing (defentity Form) in database namespace and trying to include Form on other namespace, but compiler says: Unable to resolve classname: Form

3:11 i tried with inc and require as db/Form but doesnt work. Why defentity symbols work only on same namespace?

3:12 defentity is from appengine-magic library

4:12 thorwil: markoman: i guess that's because defentity is a macro

4:13 markoman: so what you make with macro, you cant import to other namespaces?

4:13 thorwil: wait, that would affect defn, too

4:14 markoman: i made a workaround by creating a function on database namespace and by calling it I can get defentity symbols

4:15 but it feels weird why I can use symbols only inside namespace I define them

4:20 thorwil: markoman: if you do a macroexpand on a defentity call, you might see why. and maybe have to collect your jaw from the floor

4:21 markoman: heh, lets see

4:26 hmh, I think i cant use macroexpand

4:26 says: Can't take value of a macro

4:26 raek: (macroexpand-1 '(defentity ...))

4:27 * clgv updates to CCW 0.2.0 STABLE.

4:29 raek: markoman: from what I can tell (haven't used appengine myself) (defentity Foo ...) results in a (defrecord Foo ...), but also a (defn foo ...)

4:29 clgv: The new repl feels more comfortable but break the debug-repl macro I used that employs the clojure.main/repl. thats probably due to the server-repl approach I guess?

4:30 markoman: macroexpand starts with (let* [] (deftype* Form user.Form

4:31 raek: the generated type is a class, so you need to import it after you have required/used the namespace

4:31 or you can use the generated constructor function

4:32 thorwil: or perhaps consider if you can keep all the datastore business in a single file. you could call it "models.clj" ... ;)

4:33 markoman: thats what I did so far. ut yeah, this explains, type is a class and needs import

4:33 but i have another problem now, which makes no sense to me

4:34 raek: (ns the.dependant.namespace (:require the.db.namespace)) (the.db.namespace.Foo. ...)

4:34 or

4:34 (ns the.dependant.namespace (:require the.db.namespace) (:import the.db.namespace.Foo)) (Foo. ...)

4:34 note the trailing dot and the lack of a slash

4:34 (this is java interop syntax)

4:35 (ns the.dependant.namespace (:use [the.db.namespace :only [foo]])) (foo ...)

4:35 markoman: ah

4:35 raek: if you use the constructor fn instead (which is an ordinary function)

4:35 markoman: I didnt know the dot marking, that would work too

4:36 raek: I have no idea if you are supposed to use the generated type directly or if you are supposed to use the generated function

4:37 markoman: but now when I created constructor (entity-get :kw) it doenst work with (:use or (:require for some reason

4:37 maybe constructor is not good term here, but just a function to get types

4:38 raek: what I mean by constructor is a fuction (foo ...) that is a wrapper for (Foo. ...) to simplify its usage

4:40 markoman: (:require [magicforms.database :as db]) and try to call it (db/entity-get :form) I get java.lang.IllegalArgumentException: Unable to resolve classname: (db/entity-get :form)

4:42 when I get into the namespace or just use or require it on repl user namespace it works. but not when I try to compile the file

4:42 raek: sounds like there's an error in something else than the use/require mechanism

4:43 clgv: $seen lpetite

4:43 sexpbot: I have never seen lpetite.

4:43 clgv: $seen lpetit

4:43 sexpbot: lpetit was last seen quitting 6 weeks and 5 days ago.

4:43 clgv: $seen cgrand

4:43 sexpbot: cgrand was last seen quitting 3 days and 13 hours ago.

4:51 markoman: ok, I figured the error, maybe its again about macros. im using (ds/new* _type_) and I cant just get type from database.clj with the interface function... maybe I need to turn back and try importing type as a class...

5:00 thorwil: is there a nicer way to get the same result as with (defn to-int-or-nil [n] (try (Integer. n) (catch Exception e nil))) ?

5:03 fliebel: thorwil: What is wrong with that? Can n be anything?

5:04 clgv: thorwil: Integer/parseInt with surrounding try-catch is directly using java. but is it really nicer?

5:04 thorwil: fliebel: n is always a string

5:05 fliebel: In weak typed languages you could just *use* it as an integer, and PHP would probably not even comply if it wasn't numerical.

5:05 clgv: thorwil: you can specify the radix when using parseint but probably you dont need that anyway

5:07 thorwil: it just feels clunky, having to catch an exception, instead of getting nil right away. but it's short enough and does the job

5:11 clgv: how do I get the clojure 1.2.1 and clojure-contrib 1.2.1 with leiningen?

5:11 just changing the version numbers does not work

5:14 markoman: how can I map (into []) to each list element? im not sure how to pass function with arguments...

5:14 clgv: oh it works for clojure but not for contrib. so there might be noch contrib 1.2.1...

5:16 markoman: (map (fn [x] (into [] x) [...]) seems to work

5:17 thorwil: how about (map #(into [] %) [...]), then

5:34 markoman: shorter :) how to you explain that # means a function with unlimited undefined (0-n) arguments? % is the first one?

5:39 raek: clgv: true. clojure 1.2.1 and contrib 1.2.0

5:40 (the 1.2.1 version is just bugfixes)

5:41 thorwil: markoman: it's a macro that can often replace fn. but not wrapped, so no #() in a #()

5:42 markoman: ok, cool

5:42 thorwil: markoman: % is the same as %1, the first argument. following are %2, %3 ...

5:43 Chousuke: and %& for the rest arg

5:43 markoman: in which format rest of the args are, btw?

5:43 kw map?

5:43 Chousuke: a seq

5:45 thorwil: ,(#(type %&) 1 2 3)

5:45 clojurebot: clojure.lang.ArraySeq

5:49 markoman: nicely proved. hmh... next im trying to figure out, how do I sort kw maps. I have [{:x 1 :order 0} {:x 2 :order 2} {:x 3 :order 1}] and would like get in ascending order by keyword order

5:52 raek: ,(sort-by :order [{:x 1 :order 0} {:x 2 :order 2} {:x 3 :order 1}])

5:52 clojurebot: ({:x 1, :order 0} {:x 3, :order 1} {:x 2, :order 2})

5:56 raek: oh. neat: https://github.com/technomancy/slamhound

6:10 markoman: thanks

6:18 clgv: is CCW 0.2.0 stable able to reformat my files?

6:42 * clgv loves the namespace browser :D

6:46 fliebel: nice....

6:46 Java HotSpot(TM) 64-Bit Server VM warning: Exception java.lang.OutOfMemoryError occurred dispatching signal Unknown Signal to handler- the VM may need to be forcibly terminated

6:56 Has anyone figured out yet how to 'send' objects to a lazy seq? Probably something with promises.

6:58 (map deref (repeatedly promise)) smething like that probably.

7:00 raek: fliebel: http://clj-me.cgrand.net/2010/04/02/pipe-dreams-are-not-necessarily-made-of-promises/

7:01 fliebel: http://clj-me.cgrand.net/2009/11/18/are-pipe-dreams-made-of-promises/

7:01 you need some queue-like thing

7:01 fliebel: these sounds familiar… I'll read them (again), thanks :)

7:01 raek: ...which can be made with promises

7:01 fliebel: raek: Meh, I'm fed up with queues, I've been fighting them for days.

7:02 raek: his LinkedBlockingQueue version is really elegant

7:03 I love that you get synchronization automatically by only exposing the "reading end" as a lazy-seq

7:03 fliebel: raek: Okay, now try that with a PriorityBlockingQueue, and then with a SynchronousQueue :(

7:04 raek: what are the problems with a SynchronousQueue?

7:05 fliebel: raek: Nothing, except that you can't use poll or offer, nor can you lock the queue.

7:07 raek: but isn't the point of a SynchronousQueue that two threads *want* to block to transfer an object between them?

7:08 I think they're called rendezvous in some languages

7:08 why can't you lock it?

7:08 fliebel: raek: Yes, but I can't lock the whole queue to make sure I avoid racing conditions. So it's just another option that does not work.

7:09 well, if I lock it while putting, the getting function can never access it, thus the putter never finishes.

7:09 raek: does it use that object as a lock internally? if so, then simply do (let [lock (Object.)] (locking lock ....))

7:10 I ran into something similar when I tried to lock an atom

7:10 fliebel: … I'm not sure I understand what you mean.

7:10 raek: you can use another object as the lock instead of the queue

7:11 in case the-queue-as-a-lock is already used internally in the queue

7:11 fliebel: yes, but does that help? For that queue to work, 2 objects *need* to access the queue at the same time.

7:12 since put will only finish once there is a get going on at the other end, and the other way around.

7:15 clgv: fliebel: you only want to resume the putting thread if the queue is empty again?

7:16 fliebel: clgv: No, I just want the putting thread to continue putting, but the taking thread needs to know when to stop.

7:16 clgv: so you need something like a EmptyWaitHandle.

7:17 fliebel: uhm, maybe?

7:17 $google EmptyWaitHandle

7:17 sexpbot: First out of results is:

7:17 clgv: it has to wait for the queue to signal that it is nonempty

7:17 I know that the class name in .NET is WaitHandle - I don't know the java one

7:18 seems to be wait() notify() on a shared lock object in java

7:19 fliebel: take already waits for non-empty, but when the putter is done, it might wait forever.

7:19 clgv: ah ok, now I get it. so you need a timeout version of it?

7:20 fliebel: that'd work, but also be slow and ugly. I'm trying to make it JustWork™

7:22 clgv: humm you could try to couple two signals with an "or semantic" the second signal would be "putter done" which is raised when the putter calls a related method

7:23 fliebel: clgv: What I have now is that I use Thread/interrupt to quit the getter, but somehow it still hangs.

7:23 *sometimes*!

7:23 clgv: fliebel: sounds ugly. isnt this usage of threads discouraged?

7:24 fliebel: clgv: Actually it sounds a lot easier to me than the signaling you described.

7:24 clgv: for semantics of that signal coupling you can look at waithandle waitany in .net

7:24 $google c# .net waithandle waitany

7:24 sexpbot: First out of 886 results is: WaitHandle.WaitAny Method

7:24 http://msdn.microsoft.com/en-us/library/system.threading.waithandle.waitany(v%3Dvs.71).aspx

7:25 fliebel: uh, okay.

7:25 clgv: oh different first result

7:26 fliebel: anyway, I'll try the agent hack first… Then I'll need to study the Java way of notifying and locking.

7:26 clgv: but I guess it's complicated when you use the queue that hides it's own signaling for "non-empty" from you

7:27 fliebel: clgv: Actually, there is an isEmpty method...

7:27 clgv: fliebel: you can't do much with it without timeouts since your getter thread will sleep...

7:28 fliebel: :( difficult stuff, this non-clojure concurrecny.

7:31 clgv: ah well not too much. isnt the java source accessible? the concurrent queue source shouldnt be that complicated

7:32 I did write one myself sometime ago maybe not feature complete to the one you are using there ;)

7:33 you just need a list some access locking and the previously discussed signals

7:34 fliebel: ...

7:53 When a future has an error, is it done or canceled?

7:55 raek: it is done

7:57 fliebel: okay, is there a non-blocking deref, or should I just ask the future if it's done?

7:57 Or is there something like agent-errors for futures?

7:57 raek: fliebel: there is a deref with timeout in 1.3.0

7:58 but you can use the underlying .get directly

7:58 fliebel: I need to see if a future has errors without blocking it.

7:58 raek: if future-done? returns true, then you will never block on deref

7:59 fliebel: right, so (when (future-done? fut) @fut) will do just fine.

7:59 raek: and the deref will throw a ExecutionException with the exception as its cause

7:59 http://download.oracle.com/javase/6/docs/api/java/util/concurrent/Future.html

8:04 fliebel: Oh, nice :) I made seque work, except for 2 edge cases. 1) the seq throws an error 2) the provided queue already contains items.

8:08 markoman: (assoc (ds/query :kind Form) :dtype :form) says: clojure.lang.LazySeq cannot be cast to clojure.lang.Associative

8:09 is it possible to update lazy sequence similar way to assoc?

8:15 clgv: markoman: answering to the exact question - I don't think so, since a sequence is not a map

8:17 markoman: hmh... is there any way to add information on lazyseq and keep all the other information unchanged?

8:17 clgv: markoman: metadata maybe?

8:18 markoman: sequence is similar to map form for example: #:Form{:id "form1"} where I need to set :dtype

8:18 fliebel: This is starting to look really silly… I implemented a half ad-hoc, broken, informally specified, slow, bug-ridden queue to work around the problems my queue have.

8:20 avysk: markoman: I'm not sure I've got your question, but maybe lazy-cat is what you're looking for?

8:20 clgv: markoman: I didn't understand your last comment neither

8:21 markoman: well the datastore query gives this #:Form{:id "form1"} and I need to manually add :dtype so the expected return is -> #:Form{:id "form1" :dtype :form}

8:22 but I cant do assoc because of datatype im not familiar with

8:23 dnolen: markoman: how can the ds query give something that looks like that if you said it's returning a lazy sequence?

8:23 markoman: hmh, i may have understood error message wrong way

8:26 java.lang.ClassCastException: clojure.lang.LazySeq cannot be cast to clojure.lang.Associative (NO_SOURCE_FILE:0)

8:26 when I try to do this: (assoc (ds/query :kind Page) :dtype :page)

8:26 dnolen: markoman: sounds like the query is probably returning a lazy sequences of *maps*

8:27 (map #(assoc % :dtype :page) (ds/query :kind Page))

8:27 markoman: and (assoc (ds/query :kind Page) gives (#:magicforms.database.Page{:id "page1"}) oh yes

8:29 I got it now. huh, its a whole new thing to deal with sequences

8:31 freiksenet: I've replaced (list ...) with (pvalues ...) at one place and no program doesn't terminate (so it produces the result, but it doesn't end), what can cause that behaviour?

8:31 now program*

8:31 clgv: markoman: you should get and read one of the clojure books ;)

8:33 markoman: I've been passing that joy to summer time reading ;)

8:33 freiksenet: what I mean by "doesn't terminate" means it never exits, it finishes and then just hanges there until you C-c

8:33 uh, sorry for my today's english, I have bad flu %)

8:38 clgv: freiksenet: sounds like a thread is keeping the program "alive"

8:38 freiksenet: clgv: that's strange, cause it already printed the output of the program, so why would thread be still alive?

8:39 clgv: freiksenet: humm maybe the pvalues spawns some non-background thread that doesnt exist? do you have a minimal example where the error happens?

8:41 freiksenet: it was something like (apply function (list ..)) and became (apply function (pvalues ..))

8:41 clgv: I cant try it without having a complete minimal example ;)

8:42 freiksenet: ok, just a moment

8:49 raek: freiksenet: you could try calling shutdown-agents at the end of the program

8:49 pvalues uses the same threads as agents

8:50 freiksenet: ok, I'll try that

8:52 raek: ok, that worked

8:52 thanks

8:55 mm, one question. is it possible to ask clojure to muffle warnings?

9:01 fliebel: freiksenet: You mean try/catch?

9:01 oh, no, I see what you mean, I think...

9:01 freiksenet: fliebel: no. CL has *muffle-warnings* special variable

9:01 which, well, muffles warnings :D

9:01 when you set it to true

9:02 (I know it's a bad style, but I don't have time to fix import warnings now and I don't want them to show up in final jar)

9:02 fliebel: Nothing like it, as far as I know.

9:03 Does anyone know if the deref timeout is already in some snapshot or alpha?

9:05 raek: fliebel: https://github.com/clojure/clojure/commit/84710838d6996d9144d83c5b659bdeda4c656100

9:05 it should at least be in the SNAPSHOT version, if I understand the process correctly

9:06 fliebel: So that would be…. [clojure "1.0.3-SNAPSHOT"]?

9:07 uhm, switch these numbers

9:07 I think someone said the other day that snapshot builds are no longer on the default repo.

9:08 raek: How can I tell from that commit if it's in a alpha build?

9:12 raek: don't know a convenient way

9:12 there are tags for the alphas

9:13 also: semi-related http://corfield.org/clj/index.cfm

9:13 fliebel: raek: No snapshots there.

9:13 raek: [org.clojure/clojure "1.3.0-SNAPSHOT"]

9:14 thus the "semi-"... :P

9:14 fliebel: ah :)

9:14 raek: (I find that page valuable for getting a clear view of the current versions available=

9:15 and this hasn't been updated in a while: http://build.clojure.org/snapshots/org/clojure/clojure/

9:16 so the snapshots probably live somewhere else nowadays

9:16 fliebel: raek: That is what I said, someone mentioned them being moved elsewhere.

9:18 "stuartsierra: and http://build.clojure.org is no longer the host for the latest snapshots."

9:20 and http://groups.google.com/group/clojure-dev/browse_thread/thread/84a18aa37e541981

9:20 hugod_: they are on the sonatype repo I believe http://oss.sonatype.org/content/repositories/snapshots

9:22 fliebel: hugod_: Indeed, thanks :)

9:27 Waa, my cake is slow. It's been like that for weeks now. How can I get lein standalone to run in 1.3?

9:28 raek: standalone? lein repl outside a project?

9:28 markoman: how do you actually see, if item is on list? like (contains? [:form :page] :page)

9:28 fliebel: raek: jep. just to play around with some files and stuff.

9:28 dnolen: markoman: contains? is for checking keys, you want some

9:29 ,(some #{:page} [:form :page])

9:29 clojurebot: :page

9:29 raek: markoman: use (some #{the-element-you-are-looking-foor} coll). "contains?" should be read as "has-key?" and only makes sense for maps, sets and vectors (where key = index)

9:30 markoman: ok, thanks

9:30 raek: fliebel: I think you need to make a project to control that. iirc, you get whatever clojure version leiningen uses internally if you do lein repl outside a project

9:31 fliebel: okay...

9:31 raek: (it is not as configurable as cake's global project yet)

9:31 fliebel: I'd rather have cake work again…

9:32 waaa, deref doesn't even have the timeout option in the snapshot. It's not my day today...

9:40 clgv: is there a way to ":use" the methods of an entire protocol within the ns-statement?

9:53 gfrlog: If I want to have a couple sample files for my automated tests to read, where should I keep them? I was looking at the directory defaults in leingingen and saw src/test/resources, so I figured that would be sensible, but I'm not sure how to read files from there

9:53 (slurp) doesn't seem to work, while it _did_ work when I kept the file in the root of the project

9:59 joegallo: slurp take a file path as an argument, if it's a relative path, then it's relative to the cwd -- which is the root of your project. so, if you move the file into src/test/resources, then you'll need to prepend that to the path. have you done so?

10:02 gfrlog: no, I thought there would be a cleaner way to do it having to do with the classpath

10:02 I know a relatively absolute filepath would wark

10:03 fliebel: How cna I use object.wait in clojure? Since there is no synchronized.

10:03 gfrlog: my foggy impression was that the resources directory was special somehow

10:03 TimMc: gfrlog: I put my automated test files in ./res in my project folder.

10:04 lein test -> read from ./res/foo.s

10:04 joegallo: gfrlog: you can (slurp (resource "some/thing/on/classpath")), or some variation on that

10:04 might take a little tweaking

10:05 gfrlog: joegallo: that function isn't in core -- are you thinking of something in core or somewhere else?

10:05 joegallo: clojure.java.io

10:06 gfrlog: hmm

10:07 raek: fliebel: (def o (Object.)) (future (locking o (.wait o) (println "boo!")))

10:07 gfrlog: man this repl is still buggy. Aren't open source technologies supposed to fix themselves if I just wait long enough?

10:07 raek: (lokcing o (.notify o))

10:08 fliebel: raek: It works, but how can the notify execute if the future is locking o?

10:08 raek: fliebel: when it waits, it does it outside the monitor

10:08 fliebel: http://upload.wikimedia.org/wikipedia/en/f/f5/Monitor_%28synchronization%29-Java.png :-)

10:09 fliebel: oh, neat

10:09 gfrlog: joegallo: I think that is probably exactly what I was looking for, thanks

10:09 now if only src/test/resources were on the classpath...

10:09 raek: this is documented in the javadoc for java.lang.Object

10:09 gfrlog: leiningen puts resources/ on the classpath by default

10:10 gfrlog: but if that directory did not exist when the clojure instance was started, you need to restart

10:10 gfrlog: raek: the sample-project.clj claims that it adds "src/main/resource" and "src/test/resource"

10:10 joegallo: gfrlog: np, have fun

10:10 gfrlog: but based on reading the system property I think it only adds "src"

10:11 joegallo: gfrlog: you would need to add those entries to your project.clj in order for that to take effect

10:11 gfrlog: oh wait I think I may have pluralized it...

10:11 raek: gfrlog: no, that's an example of how to override the default with something elese

10:11 joegallo: the sample is just full of examples, not what is implicitly done on your behalf.

10:11 gfrlog: it has the comment ";; If you'd rather use a different directory structure, you can set these."

10:12 raek: gfrlog: it adds src/, classes/, lib/<all jars here>, resources/ and test/

10:12 gfrlog: so if the examples given aren't defaults, then I'm confused

10:14 qbg: I'm working on a fork of Clojure that tries to improve the error messages, and I'm wondering if filtering the stacktraces (more than what pst does) would be useful. Any suggestions?

10:14 gfrlog: yep, setting it in project.clj explicitly worked.

10:14 raek, joegallo: thanks

10:19 TimMc: qbg: pst?

10:20 qbg: (doc clojure.repl/pst)

10:20 clojurebot: Titim gan éirí ort.

10:20 qbg: ,(doc clojure.repl/pst)

10:20 clojurebot: Excuse me?

10:20 TimMc: OK, cool.

10:20 qbg: It is accessible in user in 1.3 at least

10:29 powr-toc: has anyone got "checkout dependencies" working in leiningen? despite having a checkouts directory it keeps going to clojars for my lib

10:30 raek: powr-toc: it needs to fetch the lib in order to fetch its dependencies

10:31 TimMc: qbg: I have `make test` -> `lein test | grep -v "at clojure"` or something. :-)

10:31 raek: a checkout replaces that project later

10:31 powr-toc: raek, but it has the project.clj in dependencies/my-lib

10:31 so does it really need to fetch it?

10:31 raek: hrm, true.

10:32 powr-toc: it seems to almost work if I don't list the project as a :dependency in project.clj, but then when I uberjar it doesn't get included

10:33 raek: what I do know is that the checkouts feature is made for the situation when the project does exist in a repo (maybe the local repo)

10:33 powr-toc: ahh ok, I'll try that

10:34 raek: you can do a lein install in the other project so that maven becomes aware of its existance

10:34 powr-toc: raek, yeah, thanks for that... I have a feeling this might work now

11:04 mprentice: can someone point me to up-to-date instructions on getting clojure, slime, and common lisp to work, or help me out? i have a git clone of technomancy's slime, version 2009-10-15, git clones of swank-clojure and clojure-mode

11:05 i am running a project with lein swank, and when i connect i get a version mismatch: swank version 20100404

11:05 also, when i quit clojure and then start another instance and connect with slime-connect, i get: "error in process filter: Symbol's function definition is void: slime-autodoc-mode"

11:11 oops, my mistake. i had setup slime-repl instead of slime-fancy, so that's the last error. still get the version mismatch but i can live with that.

11:18 edw: What's slime-fancy?

11:21 mprentice: edw: it auto-loads some slime plugins. i'm fuzzy on which ones exactly.

11:21 edw: one of them is clearly autodoc :P

11:22 edw: Thanks. My impatience trumped my laziness and I went to Google...

11:25 fliebel: Hah! Can anyone confirm I'm not daydreaming, and that this actually works? https://gist.github.com/934781 I ended up using an ad hoc, informally -specified, bug-ridden, slow implementation of half of BlockingQueue, but it seems to work.

11:30 clgv: fliebel: woah thats huge ;)

11:31 fliebel: clgv: It is. If you think you can do better… ;)

11:32 * fliebel plans to get the #clojure logs on top of google for http://www.google.com/search?q=ad-hoc+bug-ridden+half+implementation+slow+informally+specified

11:32 clgv: fliebel: no time for it now. I just did a major refactoring and have to get it to work now

11:32 fliebel: :)

11:33 clgv: lol

11:37 fliebel: ~CL

11:37 clojurebot: You just made an ad hoc, informally -specified, bug-ridden, slow implementation of half of that.

12:04 clgv: lol what?

12:04 bhenry: https://gist.github.com/a09054cec2522364dea9

12:05 for those of you using emacs

12:09 sritchie: hey all -- if I'm using the for macro to read through a bunch of lines in a file, what's the most idiomatic way to maintain a counter to keep track of line number?

12:10 the main task here is -- iterate through a text file, appending line number onto the beginning of each line

12:10 Chousuke: don't use for :/

12:10 for is lazy

12:10 clgv: sritchie: you could use map-indexed

12:11 sritchie: I was using (doseq [line (read-lines old-file)] (println (str "counter! " line))

12:11 with my output writer pointing to the file

12:12 oh, I see what you mena

12:12 wrap (read-lines old-file) in a call to map-indexed, since they're both lazy

12:13 thanks guys

12:19 pdk: ,(* 38 0.15)

12:19 clojurebot: 5.7

12:21 pdk: ,(- 39 (* 39 0.15))

12:21 clojurebot: 33.15

12:58 kaw_: Hi, I'm trying to use slime and leiningen for playing around with Clojure.. is "lein swank" supposed to make my code available at the slime REPL? I can't find my namespaces with (all-ns), am I missing something?

12:59 technomancy: kaw_: no, all-ns only gives you what's been loaded already.

13:01 bhenry: kaw_: in your repl (use 'your-ns) to make your code available at the repl

13:01 kaw_: Ah, thanks, that works

13:02 carllerche: Is it normal that when I delete a function and run C-c C-k the function is still there?

13:02 bhenry: carllerche: yes

13:03 carllerche: bhenry: is there a way to get the compilation to fail? When i rename a function, i'm tending to miss some calls.

13:03 bhenry: carllerche: http://clojuredocs.org/clojure_core/clojure.core/ns-unmap

13:05 carllerche: either use remove-ns or ns-unmap to get the unwanted things out of what's loaded.

13:05 carllerche: thanks

13:31 manutter: ,(doc ns-unmap)

13:31 clojurebot: "([ns sym]); Removes the mappings for the symbol from the namespace."

13:58 gfrlog: I wish stack traces came out backwards

14:01 bhenry: gfrlog: they are latest first. that is backwards.

14:01 gfrlog: bhenry: I wish they weren't backwards then.

14:02 because I always have to scroll up to see what's going on

14:02 fliebel: gfrlog: One, moment, I'll write you one..

14:02 * gfrlog bets nobody using emacs has this problem

14:02 gfrlog: fliebel: a lein plugin?

14:02 amalloy: they come out half-backwards. reversing them would make the problem worse, not better

14:03 fliebel: gfrlog: Nope… I'm going to do what amalloy says :)

14:03 gfrlog: fliebel: making it worse rather than better?

14:03 amalloy: you rarely want the top-most line; the most interesting stuff comes after the last occurrence of "Caused by"

14:03 fliebel: gfrlog: Yea...

14:04 gfrlog: I bet a lein plugin could filter out non-user code

14:04 then a second plugin could fix the bug that caused the stacktrace

14:04 amalloy: $google clj-stacktrace

14:04 sexpbot: First out of 382 results is: mmcgrana/clj-stacktrace - GitHub

14:04 https://github.com/mmcgrana/clj-stacktrace

14:05 amalloy: helps at least a little

14:05 gfrlog: ah this is what the ring server errors go through

14:06 amalloy: thanks

14:06 fliebel: Hm, something like… (reverse (line-seq (with-out (.printStackTrace *e))) only with-out does not exist, and Java probably does not obey *out*

14:10 gfrlog: java does not obey *out*

14:10 it is disobedient wrt *out*

14:10 fliebel: I know… ah! moment

14:13 (.setStackTrace *e (into-array (reverse (.getStackTrace *e))))

14:14 gfrlog: ^^

14:15 gfrlog: yep

14:37 kaw_: Do I need to do anything special to get my interactive session reloaded correctly after changing a record type in the source? I'm getting errors like these in the REPL, but not when compiling manually: http://paste2.org/p/1385852

14:38 Well, not in the REPL but after C-c C-k in Emacs

14:51 amalloy: kaw_: i expect you have to C-c C-k in the code defining the record and the code using the record

14:53 kaw_: Yeah, those are both in the same file

15:13 stirfoo: technomancy: do you have any suggestions for gettin *print-suppress-namespaces* working with macro expansion: C-c RET and C-c M-m

15:14 mprentice: ok, dumb question, probably a brain fart on my part. i have a list of maps sorted by :id and i want to keep the maps with unique :id. i think it's like distinct but takes a predicate. is there such a function?

15:15 hiredman: group-by :id and take the the first from each group

15:16 mprentice: hiredman: ah, thanks. sorry for the dumb algorithm question

15:16 dnolen: stirfoo: are you using swank-clojure?

15:16 stirfoo: dnolen: yes

15:17 it works fine except for the namespaces gettin printed

15:20 amalloy: if they're already sorted by :id you can use partition-by instead of group-by

15:21 stirfoo: well that and the expansion is not getting pretty printed

15:22 mprentice: amalloy: thanks. since the list is initially unsorted, i removed the sort and just use group-by

15:30 bhenry1: mprentice: is there any method to which map you want to keep? or just any one of the maps with id x?

15:31 mprentice: bhenry: any of them. i have a bunch of tokens from a tokenizing process, and a bunch of entities from a dictionary process, and i want to smush the tokens if they are in the range of an entity.

15:32 dnolen: stirfoo: it should definitely get pretty printed.

15:32 mprentice: bhenry: i may be rethinking how i do this. it's gotten so i've gone through doing this in two different ways already and each time i forget and re-invent what i'm doing :(

15:32 dnolen: stirfoo: what version of clojure?

15:52 gfrlog: is c.c.mock popular? I'm looking at the code and I think the arg-validation feature is limited to fixed-arity fns

15:53 wouldn't be too hard to patch though...

16:05 stirfoo: dnolen: 1.2.0, sorry, I stepped out for a bite to eat

16:06 I used package.el to install slime, slime-repl and clojure-mode and I think I just cloned technomancy's git repo

16:08 dnolen: stirfoo: hmm, is contrib on your path as well?

16:09 stirfoo: I *think* it is ...

16:09 dnolen: stirfoo: is it in your lib dir ?

16:12 stirfoo: I just required str-utils2 and it's working

16:13 dnolen: stirfoo: what version of clojure btw?

16:13 stirfoo: 1.2.0

16:15 I called swank.clj-contrib.macroexpand/macroexpand-all directly and it seems to branch if walk-enabled? and that depends on clojure.contrib.macro-utils

16:37 manutter: part

16:37 oops

17:12 seancorfield: someone was asking about snapshot builds:

17:12 :repositories {"sonatype" "https://oss.sonatype.org/content/repositories/snapshots" }

17:12 ,(+)

17:12 clojurebot: 0

17:13 gfrlog: ,(-)

17:13 clojurebot: java.lang.IllegalArgumentException: Wrong number of args (0) passed to: core$-

17:14 seancorfield: ,(*)

17:14 clojurebot: 1

17:14 seancorfield: ,(/)

17:14 clojurebot: java.lang.IllegalArgumentException: Wrong number of args (0) passed to: core$-SLASH-

17:15 seancorfield: ,(or)

17:15 clojurebot: nil

17:15 seancorfield: ,(and)

17:15 clojurebot: true

17:15 seancorfield: hmm, ok, now that is a bit bizarre

17:16 this is all in response to jneira on twitter being surprised that (reduce + []) => 0

17:18 gfrlog: seancorfield: which one do you think is bizarre?

17:19 seancorfield: (and) => true... but now i think about it, it makes (some) sense

17:19 gfrlog: it's analogous to (+) => 0 and (*) => 1

17:19 it lets you define it recursively with no-args as the base case

17:19 seancorfield: yeah, like i say, once i thought iabout it, it makes sense

17:20 gfrlog: it also lets you specify any number using just parentheses, + and *

17:20 amalloy: - and / don't have "obvious" meaningful defaults for the zero-arg version

17:20 gfrlog: which is good for making unsuspecting observers think you're some kind of crazy magician

17:20 seancorfield: no, that i expected...

17:21 amalloy: here's a good joke: ##(#'and true false)

17:21 sexpbot: ⟹ true

17:22 gfrlog: what happens when you eval a var?

17:22 amalloy: &(#'+ 1 2 3)

17:22 sexpbot: ⟹ 6

17:22 hiredman: gfrlog: pretty sure vars eval to themselves

17:23 gfrlog: hiredman: I would have said that too until ##(#'and true false)

17:23 sexpbot: ⟹ true

17:23 amalloy: that's why it's a good parlor trick

17:23 gfrlog: gotta start questioning somewhere

17:23 amalloy: you have to learn some internals to see why it happens

17:24 hiredman: gfrlog: vars dispatch fn calls to the value they hold

17:24 ,((resolve '+) 1 2)

17:24 clojurebot: 3

17:24 hiredman: ,(resolve '+)

17:24 clojurebot: #'clojure.core/+

17:25 gfrlog: ,((resolve 'and) true false)

17:25 clojurebot: true

17:25 gfrlog: ,(#'and true false)

17:25 clojurebot: true

17:25 gfrlog: ,(#'and false false)

17:25 clojurebot: true

17:25 raek: ah, invoking the macro as a function...

17:25 gfrlog: oh right

17:25 hiredman: ,(macroexpand '(and A B))

17:25 clojurebot: (let* [and__3468__auto__ A] (if and__3468__auto__ (clojure.core/and B) and__3468__auto__))

17:26 amalloy: raek: spoilsport

17:26 raek: ,(#'and nil nil 'x 'y)

17:26 clojurebot: (clojure.core/let [and__3468__auto__ x] (if and__3468__auto__ (clojure.core/and y) and__3468__auto__))

17:26 gfrlog: then why does it return true and not an s-expression?

17:26 seancorfield: ,#'and

17:26 clojurebot: #'clojure.core/and

17:26 seancorfield: ,and

17:26 clojurebot: java.lang.Exception: Can't take value of a macro: #'clojure.core/and

17:26 seancorfield: ah...

17:26 amalloy: gfrlog: because i passed it exactly two arguments, just like ##(and) with no args returns true

17:26 sexpbot: ⟹ true

17:26 raek: the first two arguments are special. the rest are passed to the macro normally

17:27 amalloy: &(#'and true false 'some-val)

17:27 sexpbot: ⟹ some-val

17:27 amalloy: &(#'and true false 'some-val 'another-val)

17:27 sexpbot: ⟹ (clojure.core/let [and__3468__auto__ some-val] (if and__3468__auto__ (clojure.core/and another-val) and__3468__auto__))

17:28 gfrlog: hmmm

17:28 amalloy: the fact that i pass booleans for the first two args is what makes it misleading - the and-macro doesn't read those args at all

17:28 &(#'and)

17:28 sexpbot: java.lang.IllegalArgumentException: Wrong number of args (0) passed to: core$and

17:31 chrissbx: How do I turn a file or string into a java.io.Reader ? (And how do I figure such things out myself if I don't know java already?)

17:32 raek: chrissbx: clojure.java.io/reader is very useful. it accepts Files, Strings (interpreted as filenames), Sockets, etc

17:34 amalloy: chrissbx: clojure.java.* is good in general for covering up some of the most-inconvenient java bits

17:34 chrissbx: ok, thanks

17:35 raek: chrissbx: but it is probably good to at least have scanned through the javadoc for java.io.Reader/Writer/InputStream/OutputStream

17:36 chrissbx: What's the best way to read the javadoc? I'm using emacs, not eclipse.

17:36 raek: if all you do is reading lines from text files, then you might not need to touch those directlyt

17:36 I usually just look at it in my browser (http://download.oracle.com/javase/6/docs/api/)

17:37 chrissbx: ok

17:37 raek: but if anyone has a good trick for how to do it in emacs, I'm listening... :)

17:38 technomancy: raek: I think stuarth wrote something to pull that up in w3m

17:39 raek: (there's also clojure.java.javadoc/javadoc to display the javadocs for something in the browser)

17:40 hugod_: slime-javadoc from http://bc.tech.coop/blog/081120.html

17:42 technomancy: sucks working in a language without docstrings =\

17:42 chrissbx: I want to parse a file containing clojure code, i.e. (read (open "path")) where I'm missing the open part. |read| wants a PushbackReader.

17:42 BTW how do you quote symbols in clojure? |foo| is Scheme syntax.

17:42 gfrlog: technomancy: but I'm sure the language has macros right? so you can add them easily?

17:43 chrissbx: (That is, "|foo|" is the same as "foo" in Scheme, and "|foo bar|" is parsed as 1 symbol; hence the || can be useful to distinguish symbols from other stuff in normal text.)

17:44 gfrlog: ,'"long symbol"

17:44 clojurebot: "long symbol"

17:44 gfrlog: nope

17:44 ,(symbol "long symbol")

17:44 clojurebot: long symbol

17:44 gfrlog: hmm

17:44 chrissbx: Yeah, but something straight-forward for human consumption.

17:44 gfrlog: ,'long\ symbol

17:44 clojurebot: long

17:44 gfrlog: I got nothing

17:44 chrissbx: In CL, one would upcase the symbol to differentiate it.

17:45 gfrlog: ,(doc read-string)

17:45 clojurebot: "([s]); Reads one object from the string s"

17:45 gfrlog: that oughta do for your other question

17:46 chrissbx: Yeah, only one of them.. also, I need location information, not sure that's in the result of the above.

17:46 gfrlog: location information? like filename and line number?

17:46 chrissbx: yes

17:46 and column, too

17:47 Why is http://clojure.org/reader not giving or linking the answers to all these questions?

17:51 hiredman: chrissbx: the clojure reader doesn't make file and line numbers available for symbols

17:51 raek: chrissbx: open = clojure.java.io/reader

17:52 hiredman: might be an interesting patch

17:52 I believe the current reader just adds file and line number information to lists

17:52 chrissbx: raek: no, since read doesn't work on the result of that

17:53 gfrlog: even if read or read-string will add line numbers, how would you tell it the filename?

17:54 chrissbx: how "how"? It would be kept as a field in the reader instance, for example, and then copied by read from there.

17:55 gfrlog: what's the simplest way in clojure to create a custom exception class so that I can catch my own exceptions? Having a separate file/namespace with :gen-class seems like a lot...

17:55 chrissbx: I wasn't asking you, sorry

17:55 chrissbx: Aha. Anyway I'd be fine with just line/col information, I could then add the file myself.

17:57 hiredman: why not? Does it make them available for all the other objects?

17:58 ah sorry, missed your 2 subsequent statement

17:58 s

17:59 Well, how does the clojure system itself track location information?

17:59 gfrlog: given that throwable is a class, I'm not sure I can do it with reify or deftype...

17:59 hiredman: chrissbx: doesn't

18:00 chrissbx: Well, I've not seen much help from slime backtraces, yeah, but that doesn't mean it doesn't track it, right?

18:00 And I think at least the line number is being output *sometimes*

18:00 hiredman: the line number you see is from the list form

18:01 the list (/ 1 0) is read in and gets a line number, and that line number is what you see in the divide by zero exception

18:02 gfrlog: ,(meta #'cons)

18:02 clojurebot: {:ns #<Namespace clojure.core>, :name cons, :file "clojure/core.clj", :line 22, :arglists ([x seq]), :doc "Returns a new seq where x is the first element and seq is\n the rest.", :added "1.0"}

18:02 gfrlog: so the eval function adds it to the var metadata from the list?

18:03 hiredman: no, eval has nothing to do with it

18:04 gfrlog: the reader produces lists with metadata, which eventually somehow becomes metadata on the vars?

18:04 hiredman: yes

18:05 the compiler copies from the reader metadata on (defn cons ...)

18:05 gfrlog: man there's so many pieces

18:14 chrissbx: I'm considering writing an interpreter for Clojure, to get around the problems with lost context information etc.; is there anyone who might be interested in this, who I should contact?

18:16 hiredman: chrissbx: why do it as an interpreter? just add what you want to the reader and the compiler

18:17 chrissbx: hm. I expect a mess there :~)

18:18 Well I'd reuse (and extend if necessary, but I suppose it's not) the reader.

18:18 amalloy: writing an interpreter will be much messier, i suspect

18:19 chrissbx: It would only be my own mess :D

18:20 Also, an interpreter is rather easy to write, the debugging features are likely the most involved part, and those are what I'm into, so the overhead might actually be so low that it's going to be easier than extending the compiler, and offer more possibilities.

18:20 amalloy: you want to write, maintain, and debug a whole interpreter because of a small feature you feel is missing from the compiler? that sounds like a bad case of not-invented-here; surely there's something more productive you could do

18:21 gfrlog: if you're going to do it, at least do it on a totally different platform. Erlang? javascript?

18:21 hiredman: the first thing you need is a reader that keeps column information

18:21 chrissbx: As I said, if it's more lines to write in the compiler than to make an interpreter?

18:21 As I said, the reader is not the issue.

18:21 If the reader is keeping enough location info for clojure, then I'm fine with that.

18:22 gfrlog: ,(meta (read-string "(foo)"))

18:22 clojurebot: nil

18:22 chrissbx: And if it really doesn't keep col information then I'd extend it with that, hopefully that would work?

18:22 And, I'd have to do that anyway, even when extending the compiler, so that's beside the point.

18:24 * gfrlog is not

18:24 chrissbx: gfrlog: it has to be written in Clojure so that it can call into compiled clojure code;

18:24 and Java code;

18:24 I don't want to reinvent the whole Clojure ecosystem!

18:24 hiredman: chrissbx: do you think I am deceiving you when I say it doesn't?

18:26 chrissbx: hiredman: deceiving? Nah I just don't expect it. But if you're sure, so be it.

18:57 kaw_: I think I want to make a telnet-like interface for my code (for multiple users), can anyone recommend a high-level module for doing that?

19:05 technomancy: kaw_: clojure.contrib.server-socket worked for me

19:05 clojurebot: google clojure mire

19:05 clojurebot: First, out of 74 results is:

19:05 technomancy/mire - GitHub

19:05 https://github.com/technomancy/mire

19:06 technomancy: ^^

19:07 kaw_: Thanks, I'll look at that

19:52 Somelauw: Hi, should clojure be build on pypy instead of java?

19:54 Raynes: Hi. No.

19:56 Somelauw: Since pypy is more dynamic and such than java.

20:00 TimMc: So is JS. What's your point?

20:00 brehaut: Somelauw: clojure has already worked around the discrepencies between its more dynamic nature and the JVMs more static nature. is there really much to gain vs the loss of ~20 years of VM optimisation?

20:01 Somelauw: also, clojure is gaining more static behavior over time too

20:19 amalloy: argh. how do i get access to GET parameters in compojure? i can find my POST params, and anything encoded into the URL itself like /id/10, but i want query params after the ?

20:20 hiredman: I think ring makes them available as :query-params

20:21 or, actually, it has :query-string in the spec, you use middleware to turn them into a map

20:21 amalloy: hiredman: that's a keyword. so wrap-params adds a :query-params key to the request map?

20:21 weavejester: You can just reference them directly as parameters

20:22 technomancy: weavejester: hey; having better luck with slamhound 1.1.1?

20:22 weavejester: Ring adds three keys, :form-params, :query-params and :params. The :params key is the merged map of the previous two.

20:22 technomancy, I've yet to try it :)

20:22 amalloy: weavejester: so (GET "/url/:someparam" [someparam otherquryparam] ...)?

20:22 weavejester: amalloy, exactly

20:22 amalloy: i'm still a little vague about how the ring underlying stuff works; barely know my way around compojure yet

20:23 thanks. it was astonishingly difficult to find an example of that on the web

20:23 weavejester: The bindings, when in a vector, take it from the :params key, which includes *all* kinds of parameters

20:23 There's some info in the "destructuring bindings" page on the wiki

20:25 https://github.com/weavejester/compojure/wiki/Destructuring-Syntax

20:25 amalloy: weavejester: that's...kinda true. you start from the assumption that we have a request map with :x, :y, etc in it

20:25 but it isn't clear, for someone who has never used ring outside of compojure, how to get such a request map

20:26 weavejester: amalloy, Yes... there's the first few chapters of a Ring book around that was cancelled by the publisher, and then released as CC - I'm not sure if anyone has turned it into HTML yet.

20:26 amalloy, But in a nutshell...

20:26 technomancy: the book was canceled? =(

20:27 weavejester: technomancy, Unfortunately yes :(

20:27 amalloy, Are you familiar with the concept of handlers and request/response maps?

20:28 amalloy: weavejester: a little. i watched a few presentations on ring a while ago but haven't started using any of it until last week

20:28 and i remember bits of it, but how compojure interfaces with ring is less clear to me

20:28 weavejester: amalloy, Okay, so a basic "hello world" in Ring is (defn handler [req] {:status 200, :headers {}, :body "Hello World"})

20:29 amalloy, Basically a function that returns a response as a map

20:29 amalloy: right

20:29 and takes a request as a map

20:29 weavejester: Yep. But usually you want to respond to different routes

20:29 So in pure Ring, you might write something like:

20:30 (defn handler [req] (if (and (= :get (:request-method req)) (= "/" (:uri req))) ...)

20:30 So you'd have this if-statement that would check to see if the request had the right request-method and path (URI)

20:31 With me so far? :)

20:32 amalloy: righto. and compojure (among other things), does the parsing and dispatching for me

20:32 weavejester: Yes, so Compojure does two things. The first is to add that if statement in that will only show the response if the conditions match

20:32 So (GET "/" ...)

20:32 amalloy: right

20:32 weavejester: Is the same as: (if (= :get (:request-method)) ... etc

20:33 The second part is that you probably want to pull information out of the request

20:33 So you can do that with a common binding, e.g.

20:33 (GET "/" {params :params} ...)

20:34 That's the same syntax you'll find in a let-form, or in function arguments

20:34 amalloy: except it's not wrapped up in a vector. is that because we're implicitly destructuring the ring request map?

20:34 weavejester: Compojure has two forms of destructuring

20:35 amalloy: (and nothing else exists to bind to, so there's no need for a wrapping)

20:35 weavejester: One, the standard Clojure kind

20:35 And the second is a specific Compojure kind

20:35 When Compojure gets a vector, it knows to use its on destructuring

20:36 As under normal circumstances, you can't destructure a request map using a vector destructure

20:36 amalloy: i see. so we get exactly one slot to destructure parameters in; a vector matches the :params key and does magic, while a map matches the ring request map and you do everything yourself?

20:36 weavejester: So using normal Clojure destructuring, you might write:

20:36 Yes

20:37 Vector = Compojure magic :)

20:37 Well, not magic, but it's own form of destructuring that's designed to pull information from parameters

20:37 amalloy: right, okay. and to expose those params to compojure's magic, i need to wrap-params around my handler, right?

20:37 weavejester: amalloy, Yes. In the past, Compojure did that for you

20:38 But that caused a few problems

20:38 So instead I added compojure.handler/site and compojure.handler/api

20:38 Which adds a whole bunch of common middleware for you

20:38 amalloy: yeah, i saw those

20:39 weavejester: Ring doesn't inherently know anything about parameters, cookies, sessions, or much of anything beside the raw request

20:39 amalloy: i stayed away because i didn't especially need any of them, and i'd heard someone saying something about some problem somewhere once (i can be more vague if you want)

20:39 yeah, i get the concept of middleware

20:39 weavejester: Middleware is used to parse those additional pieces of information from the raw request

20:39 amalloy: oh, haha. apparently we are using handler/site and i just didn't know

20:39 weavejester: That's basically all there is to Compojure...

20:40 The only other thing you need to know is "defroutes", which combines multiple handlers together

20:40 AWizzArd: weavejester: about the pure ring example from above: does ring only support one handler that is called for everything, and inside this one handler one needs to dispatch manually?

20:40 weavejester: If the first handler returns nil, it goes to the next one, and so on, until it gets a response map

20:41 AWizzArd, Yes

20:41 AWizzArd: ah ok

20:41 weavejester: AWizzArd, Multiple handlers that cascade are a Compojure-thing

20:41 Other routing frameworks like Moustache don't do that - they just have a static routing table (I believe)

20:41 amalloy: oh, i see. that's a surprisingly simple model of how defroutes works. i thought it was parsing the strings at macroexpansion time and expanding into a case form or something

20:41 AWizzArd: I am so used to the Compojure syntax that I never thought about how ring handles it under the hood.

20:42 weavejester: amalloy, No, it's literally just this: (fn [request] (some (fn [handler] (handler request)) handlers))

20:43 brehaut: weavejester: what do you mean by static routes table?

20:44 weavejester: brehaut, Well, like something that you can determine in one sweep. i.e. you could potentially use a static map of regular expressions for routing

20:44 brehaut, Compojure doesn't actually know whether a route matches until it evaluates it.

20:44 e.g. you could write:

20:45 (GET "/:foo" [foo] (if (= foo "bar") "Hello World"))

20:45 And that would be the same as:

20:45 (GET "/bar" [] "Hello World")

20:51 brehaut: weavejester: the equivalent in moustache would be

20:51 (app [foo] (fn [r] (when (= foo "bar") "Hello, World!")))

20:51 or perhaps more idiomatically (app [[name (fn [x] (= x "bar"))]] "hello")

20:51 weavejester: brehaut, Oh, does moustache cascade too? i.e. if one route doesn't match, it goes to the next?

20:52 weavejester, Or, rather if one route returns nil, it goes to the next

20:52 Gah, I literally talked to myself there :)

20:53 brehaut: weavejester it think so, let me just check

20:55 weavejester: brehaut, If that's the case, then withdraw my slanderous remarks about moustache ;)

20:55 Although a static routing table is probably going to be faster

20:55 amalloy: weavejester: perfect, i can read my GET requests! does compojure support the :or form for binding?

20:56 weavejester: amalloy, You mean in vector format? No, but it supports :as

20:56 what's :or again?

20:56 amalloy: weavejester: default values

20:56 weavejester: amalloy, Oh, now that would be an interesting feature to support

20:56 amalloy, But currently no, it does not.

20:56 amalloy: &(let [{k :k, v :v, :or {k 10}} {}] [k v])

20:56 sexpbot: ⟹ [10 nil]

20:57 brehaut: weavejester: it does cascade; the docs in the readme call it 'fall through'

20:57 weavejester: amalloy, That could probably be adapted to Compojure

20:57 brehaut, Ah, I stand corrected then

20:57 amalloy: weavejester: yeah, it seems like a feature you'd like to have

20:58 weavejester: amalloy, Yes, although it would have to be well documented... Because :as binds to the request, and :or would have to bind to the parameters

20:59 amalloy, The problem with Compojure's custom syntax is that it needs to be subtly different from Clojure's bindings because it's binding against a map, rather than a seq

20:59 amalloy: weavejester: is the request map anaphorically in-scope with some default name, or can you only bind to it with :as?

21:00 weavejester: amalloy, You need to explicitly bind it. Idiomatic Clojure tends to discourage implicit bindings.

21:00 e.g. (GET "/" [x y :as request] ...)

21:00 amalloy: right

21:01 weavejester: Maybe :as was the wrong keyword to use in this case

21:01 Everything else binds against the parameters, but :as goes one level up.

21:23 amalloy: weavejester: looking at http://data-sorcery.org/2009/11/29/incanter-webapp/, which seems to be using update-response from some dark corner of compojure. is that still a recommended usage? it doesn't seem very functional the way it's being used and documentation is lacking, suggesting it might be old

21:24 all i'm really trying to do is take a ByteArrayOutputStream and serve it as my :body, but it's not entirely clear from reading that tutorial how to do that

21:25 brehaut: amalloy: can you easily convert it to a file or inputstream ?

21:26 amalloy: look at png-response here http://raek.se/trattern/src/se/raek/trattern/graph.clj

21:26 weavejester: amalloy, That's not really a recommended way of doing things. Ring usually just deals in InputStreams, Files, etc - things it can consume on its own terms.

21:27 amalloy, OutputStreams are obviously things pushed to the output

21:27 amalloy: weavejester: yes, it's not hard to turn it into an inputstream, of course, since it's a byte[]

21:28 i just wasn't sure if ring supported that or what. looks like it does; thanks for the link, brehaut, that's perfect

21:29 weavejester: amalloy, It's taking advantage of an internal Ring function, so it's not really recommended.

21:29 brehaut: amalloy: https://github.com/mmcgrana/ring/blob/master/SPEC is your friend :)

21:30 weavejester: OutputStreams are tricky in general, because they're not very functional

21:30 An InputStream can almost be looked at like a lazy seq

21:30 Or could be converted into a lazy seq

21:31 It has been proposed that Ring allow a function body

21:31 e.g.

21:32 {:status 200, :headers {}, :body (fn [out-stream] (.write out-stream ...))}

21:32 But that hasn't been implemented... Partly because we might want to use a function body for asynchronous outputs.

21:33 amalloy: weavejester: it's no coincidence that scheme's version of lazy seqs is called streams

21:35 weavejester: amalloy, Yeah - my point is that InputStreams are almost functional data structures, whilst OutputStreams are very much side-effectful

21:35 amalloy, And Ring tries to be functional in general

21:36 amalloy: yes. i'm trying to serve a png, is all, and incanter gives them out as outputstreams. it's not hard to flip them around, i just was a bit confused

21:46 weavejester: gotta go - bye

23:21 seancorfield: amalloy: well, that is certainly some lateral thinking regarding problem #62!

23:21 i solved it the obvious was and then went and looked at yours :)

23:21 was = way

23:21 amalloy: seancorfield: yeah, it turns out to be shorter than the lazy-seq way

23:22 i think

23:22 seancorfield: i wasn't sure, when i solved it, but can you recur from inside a lazy-seq?

23:22 amalloy: seancorfield: you can, but you don't want to

23:22 seancorfield: that's what i thought

23:23 so i didn't try :)

23:23 amalloy: seancorfield: basically the recipe is (fn f [x] (lazy-seq (cons whatever (f more-whatever))))

23:24 i abstracted over that with https://github.com/amalloy/amalloy-utils/blob/master/src/amalloy/utils/seq.clj#L32 and wind up with something that i think is more readable

23:30 brehaut: amalloy: does 4clojure gist your solutions for you?

23:30 amalloy: brehaut: sure does

23:30 brehaut: classy

23:31 amalloy: even classier now that we only gist them if you ask us to :P. github probably didn't even notice the ~2k gists i created that nobody ever looked at, but i felt bad all the same

23:31 brehaut: heh

23:31 does it also tweet it if you choose?

23:32 amalloy: brehaut: it provides a link for you to tweet, with default text filled in

23:32 i didn't want to fuss with oauth, so it can't tweet on your behalf

23:32 brehaut: ah nice

23:32 haha

23:32 fair enough :)

23:35 amalloy: brehaut: you could find this out first-hand, you know

23:35 brehaut: amalloy: sadly im busy with work and a couple of other projects ATM

23:36 sritchie: has anyone run into this error with marginalia? clojure.lang.PersistentVector cannot be cast to clojure.lang.Symbol

23:36 mreynolds: Have you guys thought about putting that site in the title? It looks pretty cool and could encourage nice discussions

23:44 seancorfield: amalloy: i always forget you can simply name (fn)

23:45 so i tend to do (letfn [(f [..] ..)] f) - i'll remember (fn f [..] ..) in future

23:46 amalloy: seancorfield: yeah, i hate letfn

Logging service provided by n01se.net