#clojure log - Jul 30 2009

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

0:11 Jomyoot: Anyone uses BBEDIT?

0:32 mebaran151: any hack to assign metadata to a java object

0:32 it would be extremely convenient in this one case

2:23 Fossi1: freakin damn lazyness

2:23 it got me *again*

2:23 Raynes: Fossi1: In order to beat the lazy, you must first, think like the lazy.

2:24 Kick your feet up, grab a remote and a cup of coffee and put your hands behind your head for an hour or too. Once you have done this, you will be ready.

2:25 _mst: when someone asks you to do something, just immediately say "Yep, done". Then when they actually want to see the work start madly scrabbling...

2:25 people will respect you for it :)

2:26 Fossi1: hell, i can think lazy ;D

2:27 normally it's called procrastination though

2:30 albino: _mst: I like that advice

2:30 Fossi1: lisppaste8: url

2:30 lisppaste8: To use the lisppaste bot, visit http://paste.lisp.org/new/clojure and enter your paste.

2:31 Fossi pasted "ref trouble" at http://paste.lisp.org/display/84421

2:32 Fossi1: when i call this, i get [ref{} ()] back, then when i run *1 on the repl again, ref suddently has a value

2:34 hiredman: are you sure you want commute there?

2:34 Fossi1: could be ref-set, i don't care what color it is, if i have 2 such events

2:35 just why does the ref change after the dosync is done?

2:35 because everything around it is lazy?

2:35 do i need to doall the map?

2:35 hiredman: yeah

2:36 you might just put (vec left-events) in the returned pair

2:38 Fossi1: ok, i'll try

2:45 ok, didn't help much with my error, i'll have a look later

2:45 off to more professional clojure ventures ;)

2:45 bbl

3:32 lbj: Top of the morning gents

3:44 AWizzArd: Hi lbj

3:49 lbj: Any interesting new developers in Clojureland lately? (disregarding new new entirely)

3:50 hehe, developers = developments

3:52 Chousuke: well, the new forkjoin stuff looks promising, and "batch mode"

3:52 lbj: What are they?

3:54 @ Chousuke

3:54 Chousuke: http://paste.lisp.org/display/84027 <- automatically parallelised vector operations and http://paste.lisp.org/display/84027 (no timing data there though) :/

3:55 I wonder which branch the batch stuff was in.

3:56 lbj: Interesting - Does vec then determine wether or not the task at hand is suited for paralization ?

3:56 Chousuke: vec does nothing different. it's pvreduce and pvmap that do the parallelisation

3:58 rhickey says he has a quad core machine, so the results are rather impressive.

3:58 lbj: Indeed

3:58 Chousuke: I have a dual core and wasn't able to get 2x improvement :/ I suspect my memory is a bottleneck

3:58 lbj: But if you replace vec with pvreduce, what would the answer to my question be?

3:59 Chousuke: pvreduce always divides the work I think

3:59 lbj: There's a certain threshold before which paralization would only slow the process down with scheduling. If this is automated it has to be aware of that somehow

4:00 Chousuke: the batch mode stuff is also interesting; it allows you to get a local "mutable" version of a collection for cases when you know you will be doing lots of modifications in one go and don't need the intermediates.

4:00 lbj: it's not that far automated :)

4:01 regular reduce and map will remain (the pv* stuff is not lazy anyway)

4:05 lbj: There's also the chunked seqs branch, but you may have already heard of that :)

4:06 lbj: That I'm familiar with

4:18 Chousuke: lbj: I hope this doesn't explode your ERC

4:18 lbj: Thanks friend - I actually got one of those cool Jaunty notifications :)

4:19 Now I just need to work out the hook for private msgs, seems thats handled differently

4:26 blbrown_win: morning

4:26 lbj: Morning :)

4:37 blbrown_win: I wish I got paid for the work I did outside of work

4:38 hiredman: heh

4:39 Raynes: I wish I got paid for the work that hiredman does outside of work...

4:39 blbrown_win: nice

4:39 me too

6:00 angerman: I have two vecotrs, the first one consists of bools and has exactly one set to true. The second one has values.

6:00 now I want to get the value that corresponds to the true item

6:01 any ideas?

6:01 ,(let x [true false false] y ["a" "b" "c"] (for [x x y y :when x] [x]))

6:01 clojurebot: java.lang.IllegalArgumentException: let requires a vector for its binding

6:07 jdz: ,(mapcat (fn [x y] (when x (list y))) '[true false false] '["a" "b" "c"])

6:07 clojurebot: ("a")

6:07 angerman: ,(:true (zipmap [:true :false :false] ["a" "b" "c"])

6:07 clojurebot: EOF while reading

6:07 angerman: ,(:true (zipmap [:true :false :false] ["a" "b" "c"]))

6:07 clojurebot: "a"

6:07 angerman: does not feel right :/

6:08 lbj: angerman: Why are you using a vector?

6:08 angerman: lbj: ?

6:09 hiredman: ,((comp second first (partial filter first) (partial map vector)) '[true false false] '["a""b" "c"])

6:09 clojurebot: "a"

6:10 lbj: angerman: It sounded like a job for a map, but anyway, merge the two, maybe applying partition 2

6:10 hiredman: ,(zipmap '[true false false] '["a" "b" "c"])

6:10 clojurebot: {false "c", true "a"}

6:10 hiredman: ,((zipmap '[true false false] '["a" "b" "c"]) true)

6:10 clojurebot: "a"

6:10 hiredman: I think I win

6:10 angerman: hiredman: ahh, right

6:10 hiredman: :) I tried (true (zipmap ...)) and that didn't work

8:45 lbj: ...

10:51 dysinger: moing!

10:53 cgrand: dysinger: shhh everybody is asleep

10:55 * ttmrichter eyes dysinger.

11:26 Drakeson: how would you find the element in a list that has the maximum value of a function of the element? (reduce max (map f coll)) only gives you the maximum value, not the one that maximizes f.

11:28 stuartsierra: I think there's a contrib fn that does that

11:28 rhickey: ,(doc max-key)

11:28 clojurebot: "([k x] [k x y] [k x y & more]); Returns the x for which (k x), a number, is greatest."

11:29 Drakeson: thanks :)

11:29 * Drakeson treasures max-key

13:09 * rhickey git push origin new

13:09 rhickey: new new branch is up

13:10 stuartsierra: cool

13:10 rhickey: still work in progress, but can create new instances - all method need full type hints (missing hint means Object)

13:11 will eventually try to match superclasses' sigs

13:11 does accept non-interface superclass with no-arg ctor

13:12 primitives can flow in and out

13:12 no indirection on method calls

13:13 (new [super? interfaces*] thisname? {:flags :here}? (methodname [args] body)*)

13:14 stuartsierra: "thisname" is a class name?

13:14 rhickey: hints are metadata on method and arg names

13:14 stuartsierra: no, the name of this object, usually this

13:14 stuartsierra: ah, ok

13:16 rhickey: (defn foo []

13:16 (let [y (int 17)]

13:16 (new [Object clojure.lang.Seqable] this {:foo :bar}

13:16 (#^String toString [] "foo")

13:16 (#^int hashCode [] 42)

13:16 (#^int baz [#^int x] (+ x y))

13:16 (#^clojure.lang.ISeq seq [] (seq ["woo hoo"])))))

13:17 Chousuke: are the flags purely for the compiler or can you access them somehow?

13:18 rhickey: Chousuke: flags go in bitbucket right now

13:18 Chousuke: heh.

13:20 stuartsierra: So this returns an instance of a new dynamically-generated class, which is not a proxy, correct?

13:20 rhickey: right

13:20 should be much faster than proxy

13:20 stuartsierra: Gotcha.

13:20 And it is not intended to replace gen-class for statically-named classes?

13:20 rhickey: not right now

13:20 stuartsierra: ok

13:20 duck1123: will this replace proxy, then?

13:21 rhickey: duck1123: most usage will move to this, but not removing proxy

13:21 proxy does support some dynamic fiddling which this won't

13:22 not much used afaik

13:22 stuartsierra: Right, this "new" creates a class which cannot be changed, correct?

13:22 rhickey: right

13:22 stuartsierra: I didn't realize proxies could be changed.

13:22 rhickey: but the enclosing 'factory' fn can, since no one depends on the name of the generated class

13:23 ,(doc update-proxy)

13:23 clojurebot: "([proxy mappings]); Takes a proxy instance and a map of strings (which must correspond to methods of the proxy superclass/superinterfaces) to fns (which must take arguments matching the corresponding method, plus an additional (explicit) first arg corresponding to this, and updates (via assoc) the proxy's fn map. nil can be passed instead of a fn, in which case the corresponding method will revert to the default behavior

13:25 stuartsierra: Wow, never saw that before. Can't think I'd use it much; I'd rather call a normal fn and rebind that.

13:25 Chouser: rhickey: so cool. Are the return type hints required for some reason?

13:25 rhickey: still todo - figure out signatures from superclasses, prevent additional overloads of superclass methods (overrides only, local helper methods must have unique names), volatile declarations, non-reflective calls to this...

13:26 Chouser: ah

13:26 rhickey: stuartsierra: the advantage of that is it updates existing instance

13:26 stuartsierra: Cool, like prototype classes.

13:27 rhickey: stuartsierra: well, it's per instance

13:27 stuartsierra: Ah, like Ruby's instance methods, then. :)

13:27 * stuartsierra searches for metaphors...

13:27 Chouser: update-proxy is per instance, delegating do a var and redef or binding that would be per class, and newnew won't do either.

13:27 rhickey: like I said, I don't know if anyone uses it

13:28 Chouser: to a var

13:28 rhickey: new new will be what most people imagine proxy is

13:28 lbj: Good evening gents

13:29 rhickey: anyway, what's up there should be usable, and needs a good workout

13:29 stuartsierra: Same usage pattern as an anonymous inner class in Java, right?

13:30 rhickey: stuartsierra: sort of, more emphasis on the lexical binding, less on nested class scope

13:30 stuartsierra: ok

13:30 rhickey: but supports multiple supers, unlike aic

13:30 stuartsierra: right, forgot that detail

13:31 rhickey: no local member not from lexical scope, i.e. no explicit fields, field inits, class init...

13:32 will support volatile declaration making for mutable local in 'this' only

13:32 I would like a good name for these things other than "kind of like an anonymous inner class" though

13:32 :)

13:33 lexical objects?

13:33 duck1123: so does "old" new still work the same way, or will usage of it need to be changed?

13:34 not that I ever use new, I always do (Object.)

13:34 rhickey: ,(macroexpand-1 '(Object.))

13:34 clojurebot: (new Object)

13:35 lbj: The questions still good :)

13:35 rhickey: (new classname ...) works as ever, does something different only when it sees vector as first arg

13:36 Chouser: lexical object isn't bad. I was thinking Clojure object would work.

13:36 rhickey: Chouser: I want to avoid Band-Aid/Kleenex syndrome

13:36 lbj: Ok, sounds good

13:37 Chouser: other languages have lexical objects, but it doesn't seem like there's a whole lot of agreement on what they actually are.

13:37 duck1123: I knew that (Object.) expanded to (new Object) but presumably if the way to call (new Object) changed, the macro expansion would be changed to the new format

13:37 stuartsierra: "temporary-class object"?

13:37 rhickey: Chouser: right, completely different meanings

13:37 stuartsierra: "generated object"?

13:37 y-combinator: Hello. What is the best way to comnvert lazy seq to vector?

13:37 Chouser: y-combinator: vec

13:38 stuartsierra: "anonymous instance"?

13:38 Chouser: you actually get both a class and an object

13:39 rhickey: please try replacing a proxy or two with new new, admittedly the required hints make it a bit of a pain right now

13:39 stuartsierra: "generated class instance"?

13:39 rhickey: Chouser: right, that's the problem

13:39 Chouser: ooh, sure, I have proxy all over the place. hmm, where to start...

13:40 hm. need ctor args

13:40 lbj: ~proxy

13:40 clojurebot: proxy is <Chouser> proxy teases with its ease of use, then suddenly betrays.

13:41 lbj: To me proxy was downright unuseable, because it could extend far enough

13:47 Chouser: 'read' requires a PushbackReader; PushbackReader requires an arg for its ctor; have to keep using proxy here I guess.

13:55 Second attempt, this time extending sun.misc.Signal:

13:55 Exception in thread "SIGINT handler" java.lang.AbstractMethodError: clojure.contrib.repl_utils$eval__335$start_handling_break$obj__339.handle(Lsun/misc/Signal;)V

13:57 (new [sun.misc.SignalHandler] this (#^Void handle [sig] (prn :got-it) nil))

13:59 Chousuke: Void? not void?

14:00 Chouser: ,#^Void []

14:00 clojurebot: []

14:00 Chouser: ,#^void []

14:00 clojurebot: java.lang.Exception: Unable to resolve symbol: void in this context

14:00 * Chouser shrugs

14:00 Chousuke: hmm

14:00 ,(str Void/TYPE)

14:00 clojurebot: "void"

14:01 stuartsierra: (.getName (.getParent Void))

14:01 ,(.getName (.getParent Void))

14:01 clojurebot: java.lang.IllegalArgumentException: No matching field found: getParent for class java.lang.Class

14:06 rhickey: #^void in new new

14:08 Chouser: hm. same error though.

14:08 (.handle (new [sun.misc.SignalHandler] this (#^void handle [sig] nil)) nil)

14:08 stuartsierra: do you need to type-hint sig?

14:09 rhickey: Chouser: sig is an Object?

14:09 Chouser: yes

14:09 (.handle (new [sun.misc.SignalHandler] this (#^void handle [sig] nil)) (sun.misc.Signal. "INT"))

14:09 rhickey: there's no default method for methods yo udon't define

14:10 if they get called you'll get AbstractMethodError

14:10 Chouser: (.handle (proxy [sun.misc.SignalHandler] [] (handle [sig] nil)) (sun.misc.Signal. "INT")) ;works fine

14:10 rhickey: ok, good to know. sun.misc.SignalHandler has only the one instance method

14:12 rhickey: Chouser: sig is Signal, no?

14:12 #^Signal sig

14:12 Chouser: yes, sun.misc.Signal

14:12 rhickey: then it's not Object

14:12 Chouser: oh, sorry. I thought you meant non-primitive

14:13 rhickey: sorry I said an Object before, the sig is not Object

14:13 Chouser: ah, so I do have to hint the arg

14:13 rhickey: right now you have to specify exact signatures, I'm not looking at superclasses at all

14:13 Chouser: ok

14:13 rhickey: it won't end up being this hard, sorry

14:14 Chouser: ok! works perfectly.

14:15 so proxy will continue to be useful for when you need to extend classes that require args in their ctors?

14:16 rhickey: Chouser: I need to see when/why people are doing that. Deriving from concrete classes is always a questionable design, java.io a perfect example of how bad it can get

14:17 Chouser: ok

14:17 in this case (repl-utils/get-source) I only need it because core/read needs a PushbackReader

14:18 Chousuke: hmm

14:18 are there any OO languages that allow only interface inheritance?

14:18 rhickey: But looking at the difference between new new and anonymous inner classes is instructive, all that implicit complexity we become inured to

14:19 Chouser: It's a real pain that there aren't interfaces for java.io

14:19 but when does it stop?

14:20 Chousuke: is java.nio better in this regard? :/

14:20 rhickey: even the no-arg concrete ctors are insidious

14:22 Chouser: Does java.nio have anything line-based?

14:24 oh, I guess that's not what 'read' cares about. It wants an unread() method.

14:25 stuartsierra: What's the alternative to no-arg ctors? Factories?

14:26 rhickey: stuartsierra: it's actually more about concrete derivation than the ctors

14:26 stuartsierra: right, ok

14:27 Chouser: I guess clojure could define an interface with unread(), and by default use a custom class that extends PushbackReader. That would allow me to use newnew to implement a different class that 'read' would be happy with.

14:27 Chousuke: IClojureReader? :P

14:27 rhickey: Chouser: it could, yes

14:27 IMTiredOfJavaIo

14:28 Chousuke: :)

14:28 Chouser: with all that, repl-utils could abandon proxy

14:28 Chousuke: Probably it would help with clojure-in-clojure as well?

14:28 stuartsierra: I remember when I was trying to write c.c.fnmap, I discovered I couldn't derive from PersistantHashMap. Maybe I wasn't supposed to.

14:29 rhickey: stuartsierra: no, there are interfaces + abstract bases

14:29 hiredman: clojurebot: a man of means by no means is <reply>da da king of the road

14:29 clojurebot: Alles klar

14:30 Chousuke: if you define the clojure reader in terms of Clojure interfaces, it would be more portable across hosts too, I guess.

14:30 rhickey: I'd love for a different solution than abstract bases, but they don't have (most of) the problems of concrete derivation

14:31 and I can make it so all of mine have no-arg ctors. The other set of very valuable ones, for j.u.collections, also have no-arg ctors

14:34 Chouser: oh, so 'this' is not required, but if not given you have no way to refer to the current instance.

14:34 clojurebot: this is not a bug

14:35 Chouser: no indeed

14:35 rhickey: right, it can get as small as: (str (new [] (#^String toString [] "foo")))

14:35 and once I get the superclass stuff in, lose the type hint

14:35 hiredman: ooo

14:36 stuartsierra: that's nice, that will be really useful.

14:36 You could really do prototype-style objects then, right? Just define a "constructor" function that calls newnew.

14:37 Well, I guess you can't change the definition of existing objects, but everything else.

14:37 hiredman: stuartsierra: I doubt that

14:37 Chouser: you can do even closer to that now with proxy, I'd think

14:37 hiredman: seems like new has to be a compile time operation

14:38 rhickey: stuartsierra: it's more a rendition of known good practice - factory fns hiding implementation class details, programming to interfaces only

14:38 hiredman: which makes it hard to monkey with at runtime

14:39 stuartsierra: But you could gen-class a superclass if you need static names or mutable definitions, right?

14:39 e.g., (gen-class "x.y.z") ... then later (new [x.y.z] ...)

14:40 hiredman: gen-interface :P

14:40 Chouser: exactly

14:40 stuartsierra: cool!

14:40 Chouser: proxy already gives you a whole lot of this

14:41 stuartsierra: True.

14:41 rhickey: stuartsierra: right gen-class will do all of that. I'm still thinking about both stable anonymous names (for serialization) and AOT-only static names, but the best code will gen-interface + new new, possibly + gen-static which will make Java-friendly static-member-only classes

14:41 Chouser: Mmmm.. I want gen-static.

14:41 stuartsierra: Nice.

14:41 kotarak: what I don't like about proxy, is that it's saves its .class file in a common place for different namespaces. This makes packaging a nightmare. :(

14:42 Chouser: I have a gen-class with a whole lot of #^{:static true}

14:43 rhickey: people make such a righteous mess of OO, but if you only ever have immutable instances of anonymous classes manipulated via interfaces, life is good

14:43 so I'm trying to encourage that

14:44 and most of Clojure's Java is that way, except the few places where I need to define constructs to manage mutation or laziness

14:44 stuartsierra: Great idea. A pity it breaks down as soon as you need to do I/O. :)

14:44 hiredman: java.lang.NoSuchMethodError: clojure.lang.RestFn.<init>(I)V (fnparse.clj:1)

14:44 Chouser: kotarak: interesting. would you want a classpath entry per namespace, and somehow tell proxy that?

14:44 hiredman: :(

14:44 kotarak: chouser: just a sec

14:46 rhickey: stuartsierra: I think Chouser has a good idea replacing PushbackREader in Clojure's interfaces. HAving a concrete class in a signature is always a bad sign. But the drawback is that PushbackReader itself won't implement our interface, people will always have to implement our thingy, which is also bad, but not as bad

14:46 and supports implementing the interface via composition, which is good

14:47 Chouser: I suppose 'read' could still accept PushbackReader as well

14:47 stuartsierra: I suppose it's trivial to wrap java.io.PushbackReader (or whatever) in clojure.lang.BetterReader.

14:47 rhickey: as soon as there is an interface you can compose

14:48 and with new new implement macros like def-this-on-that

14:48 stuartsierra: Right, I was just thinking about some kind of "thin proxy" that would delegate from an interface to a concrete class with matching methods.

14:48 rhickey: and automate all the forwarding associated with composition

14:49 Chousuke: what methods does the clojure reader need? just read and unread?

14:51 Chouser: Chousuke: I think that's right.

14:52 Drakeson: liebke: a suggestion re incanter, could you please remove dependencies from lib/ and put them in cloud.github, as compojure does? thanks :)

14:56 hiredman: oooh

14:57 new-new works in clojurebot's sandbox

14:57 rhickey: ,(str (new [] (#^String toString [] "foo")))

14:57 clojurebot: "foo"

14:57 rhickey: cool

14:58 let the AbstractMethodErrors ensue

14:59 hiredman: ,((new [clojure.lang.IFn] (#^Integer invoke [#^Integer x #^Integer y] (+ x y))) 1 2)

14:59 clojurebot: java.lang.VerifyError: (class: sandbox$eval$obj__1939, method: invoke signature: (Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Integer;) Wrong return type in function

14:59 hiredman: :|

14:59 rhickey: use #^int etc

14:59 hiredman: oh

14:59 ,((new [clojure.lang.IFn] (#^int invoke [#^int x #^int y] (+ x y))) 1 2)

14:59 clojurebot: java.lang.AbstractMethodError: sandbox$eval$obj__1945.invoke(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;

15:00 hiredman: ah

15:00 doesn't match

15:00 I see

15:00 rhickey: ,((new [clojure.lang.AFn] (invoke [x y] (+ x y))) 1 2)

15:00 clojurebot: 3

15:01 Chousuke: hmm

15:01 hiredman: ,(import '(clojure.lang IDref AFn IFn))

15:01 clojurebot: java.lang.ClassNotFoundException: clojure.lang.IDref

15:01 hiredman: ,(import '(clojure.lang IDeref AFn IFn))

15:01 clojurebot: clojure.lang.IFn

15:01 rhickey: I came up with a plan for supporting long and double args and returns in IFn earlier this week, but there are only so many hours in a week

15:01 IDeref

15:01 hiredman: Excellent

15:02 ~rhickey

15:02 clojurebot: is_rhickey_is_a_minor_god? is yes

15:02 Chousuke: funky predicate

15:02 :P

15:03 * rhickey goes back to reimplementing genclass' non-private-methods in Java, sigh

15:03 gjohnson: howdy rich. I'm wondering about pcalls in clojure.core

15:03 rhickey: gjohnson: hi

15:04 gjohnson: At first glance, I would have imagined it was part of jsr166y.

15:04 but apparently not. so I'm guessing you're doing some magic with threadpools or somesuch?

15:04 hiredman: rhickey: so new-new methods aren't backed my fns?

15:04 rhickey: gjohnson: it's just piggybacking on the agent thread pool

15:05 gjohnson: ok, that was my guess.

15:05 rhickey: hiredman: no, pedal to the metal methods

15:05 gjohnson: there is work going on in the par branch to leverage jsr166y, including a fjtask macro

15:05 hiredman: do they have a frame to recur to?

15:05 rhickey: returns something that can be forked and joined

15:06 hiredman: yes, should do

15:06 gjohnson: hmm...so will that ultimately give pcalls a dependency on jsr166y and move it out of core?

15:06 hiredman: I guess I could have just tried that out :P

15:07 rhickey: gjohnson: I haven't decided on tying the two together, so probably some new features for fj

15:07 gjohnson: I had built a little tool for splitting my work over multiple processors back in January or so, but some of it was guesswork because I don't know the formula you're using to calculate the size of the fixed thread pool.

15:07 stuartsierra: number of cores reported by the runtime

15:07 gjohnson: ,(defn distribute-load-over-processors

15:07 [action-fn arg-seq]

15:07 (let [num-processors (.availableProcessors (Runtime/getRuntime))

15:07 agents (map agent (replicate (* *agents-per-processor* num-processors) ()))]

15:07 (println "Sending Tasks to" (count agents) "Agents...")

15:07 clojurebot: EOF while reading

15:07 gjohnson: (dorun (map #(send %1 action-fn %2) (cycle agents) arg-seq))

15:07 (println "Waiting for Agents to Finish...")

15:07 (apply await agents)

15:07 (apply concat (map deref agents))))

15:07 bah

15:08 hiredman: :(

15:08 gjohnson: looks like the code didn't make it through unscathed. *sigh*

15:08 Chousuke: lisppaste8: url

15:08 lisppaste8: To use the lisppaste bot, visit http://paste.lisp.org/new/clojure and enter your paste.

15:08 stuartsierra: try a pastie

15:09 gjohnson: alrighty. the , prefix only works for one-liners then?

15:09 stuartsierra: yep

15:09 Chousuke: and you can't defn anyway :p

15:10 lisppaste8: gjohnson pasted "distribute-load-over-processors" at http://paste.lisp.org/display/84459

15:10 gjohnson: indeed, that would be a bit of a security hole.

15:10 ;)

15:11 Chousuke: well, you can get around the protections anyway

15:11 but as far as I can tell, all you can do is DOS clojurebot :P

15:11 gjohnson: hmm...

15:11 stuartsierra: ,(loop [x 1] (recur (inc x))

15:11 clojurebot: EOF while reading

15:11 Chousuke: to take over the machine itself, you'd have to break java's sandboxing.

15:12 stuartsierra: ok, I'll play nice, won't retype that

15:12 * hiredman should run clojurebot in a chroot

15:12 rhickey: ,(.down (new [] (down [x] (if (zero? x) x (recur (dec x))))) 42)

15:12 clojurebot: 0

15:12 gjohnson: well, now that that's pasted, I was wondering if someone (rich?) could take a look at the code and tell me how different (for better or worse) this function is as compared to pcalls.

15:12 hiredman: rhickey: that is awesome

15:12 Chousuke: ,(loop [x 1] (recur (inc x)))

15:12 :p

15:12 hiredman: ♥ new new

15:12 clojurebot: Execution Timed Out

15:13 stuartsierra: Clever clojurebot.

15:13 hiredman: ~def pcalls

15:13 gjohnson: apparently so.

15:13 ah, pretty.

15:13 hiredman: uh oh

15:14 gjohnson: that url's not working for me.

15:14 Chousuke: gjohnson: you might want to use futures instead of agents; agents have a thread pool anyway.

15:15 kotarak: Chouser: sorry. Had to operate the musicbear. I'm not in the details how proxy works. But if it generates a class clojure/proxy/my/Super.class, my naive view would say that it should be possible to generate a class named foo/bar/clojure/proxy/my/Super.class, no?

15:15 gjohnson: as noted. that's why I don't spawn any threads.

15:16 I just create a number of agents that are a dynamically bound multiple of the thread pool, and map calls to send across a cycled sequence of the agents.

15:17 hiredman: ah

15:17 gjohnson: this should just queue up all the functions more or less evenly across my agents and let the fixedthreadpool handle them.

15:17 hiredman: because that rev is on the new-new branch

15:17 or not

15:17 Chouser: kotarak: ah! hm, I don't see why it couldn't.

15:18 kotarak: Then packaging would be easy. foo/bar/* goes to that .jar, foo/baz/* to that other one.

15:18 Chouser: rhickey: ,(.no-frickin-way (new [] (#^String no-frickin-way [] "no, really!")))

15:18 clojurebot: new Class(x) is (Class. x)

15:18 kotarak: Without fiddling with the proxy classes. (Which namespace calls which proxy? Hmm... Bookkeeping ahead...)

15:18 Chouser: ,(.no-frickin-way (new [] (#^String no-frickin-way [] "no, really!")))

15:18 clojurebot: "no, really!"

15:18 gjohnson: my concern is that if I make less agents that the thread pool size, I'll end up with unused threads.

15:19 Chouser: rhickey: will the ability to create new methods like that go away?

15:20 hiredman: grr

15:23 * kotarak doesn't get what all the people use eval for...

15:24 hiredman: kotarak: it's lisp! it has eval! and macros! YEAH!

15:26 rhickey: Chouser: no, that will stay, to allow for the definitions of helper methods. Calling them from outside will always involve reflection though

15:27 they will have to have names different from those of the supers' methods, and overloading on arity only

15:27 Chouser: oh, interesting.

15:28 runtime reflection required to call other instances of the same class?

15:28 rhickey: Chouser: yeah, anything other than this

15:30 the dashes won't work on all JVMs

15:30 so I'll have to filter those

15:31 ,(class (new []))

15:31 clojurebot: sandbox$eval$obj__1918

15:37 Chouser: ,(new [] (#^String toString [] (str "what's wrong?")))

15:37 clojurebot: java.lang.VerifyError: (class: sandbox$eval$obj__1923, method: toString signature: ()Ljava/lang/String;) Wrong return type in function

15:38 hiredman: ,(class (str "what's wrong?"))

15:38 clojurebot: java.lang.String

15:38 hiredman: :/

15:38 ,(new [Object] (#^String toString [] (str "what's wrong?")))

15:38 clojurebot: java.lang.VerifyError: (class: sandbox$eval$obj__1932, method: toString signature: ()Ljava/lang/String;) Wrong return type in function

15:39 Chouser: the compiler should even know str returns a String

15:39 hiredman: ,(new [] (#^String toString [] (.toString (str "what's wrong?" "foo"))))

15:39 clojurebot: #<sandbox$eval$obj__1952 what's wrong?foo>

15:41 hiredman: ,(str (new [] (#^String toString [] (str "what's wrong?")))

15:41 clojurebot: EOF while reading

15:41 hiredman: ,(str (new [] (#^String toString [] (str "what's wrong?"))))

15:41 clojurebot: java.lang.VerifyError: (class: sandbox$eval$obj__1981, method: toString signature: ()Ljava/lang/String;) Wrong return type in function

15:41 trotter: are there any good code coverage tools for clojure?

15:42 hiredman: ,(.foo (new [] (#^String foo [& bar] "foo")) 1 2)

15:42 clojurebot: "foo"

15:43 hiredman: ,(new [] (#^String toString [] #^String (str "what's wrong?")))

15:43 clojurebot: java.lang.VerifyError: (class: sandbox$eval$obj__2016, method: toString signature: ()Ljava/lang/String;) Wrong return type in function

15:43 hiredman: :(

15:43 it's too bad

15:45 lousey Object specifying interface

15:47 rhickey: Chouser: fixed: http://github.com/richhickey/clojure/commit/3c15d0eb6bdc279c801dd984a3524666d7c5cfbe

15:53 Chouser: rhickey: Thanks! That's every proxy in contrib, except the read thing: http://n01se.net/paste/knS

15:55 rhickey: wow

15:56 does it still work?

15:59 Chouser: yes

15:59 er, every proxy of mine

16:00 rhickey: will be much nicer without the hints

16:00 Chouser: there are still proxies in swing_utils and singleton

16:00 yes, it definitely will.

16:21 Fossi: so, to come back to my question of tomorrow, if i deref a ref, it's contents doesn't get visited, right?

16:21 so i get whatever state the ref might be in?

16:22 s/tomorrow/this morning

16:22 * danlarkin wonders if Fossi wrote a time machine in clojure

16:22 hiredman: eh?

16:22 Fossi: http://paste.lisp.org/display/84421

16:22 hiredman: "it's contents doesn't get visited" what does that mean?

16:23 Fossi: "hiredman: you might just put (vec left-events) in the returned pair"

16:23 hiredman: Fossi: I remember saying that

16:23 Chousuke: Fossi: camelCase is not very clojurey :p

16:23 Fossi: you should use hyphens with lisp functions.

16:24 Fossi: so if on the left, i return @state-ref, i get {} mostly

16:24 Chousuke: ups, all the Java in that app tends to confuse me :)

16:24 Chousuke: also, I'd recommend not putting any actual dosync logic in your functions.

16:24 Fossi: that i don't understand

16:25 hiredman: Fossi: I still want to know what "it's contents doesn't get visited" means

16:25 Fossi: well, i deref the ref, but i get {} (for example on the repl)

16:25 Chousuke: Fossi: instead of going a dosync in change-color, write all the transitions as pure functions and then call (dosync (alter state change-color)) in some function that contains most of the "non-functional" logic

16:25 hiredman: and?

16:25 Fossi: if i use *1, i suddently get a value

16:26 hiredman: Fossi: print out the second item in the return vector of calcGameState forces the map

16:26 map being lazy

16:26 Fossi: yeah, somehow feels weird

16:26 same for try catch really

16:26 hiredman: the functio being mapped as a side effect of changing the ref

16:27 so the ref does not get changed unless you force the map

16:27 Fossi: but i guess that's Chousuke's point

16:27 Chousuke: Fossi: What I'm advocating is isolation of side-effects. ref alterations are side-effects :)

16:27 Fossi: Chousuke: well, i could have another layer there, yes

16:28 but then, i don't really know what implementors of dispatchEvent might want to do to the state

16:29 hiredman: anyway

16:29 you should force the map

16:30 Chousuke: why are you returning a new state-ref though?

16:30 in calcGameState

16:31 Fossi: i return @state-ref now actually

16:31 state-ref was just to test it from the repl

16:32 the idea is to have a load of stm calculations being done to get to the new game state for the "mutable" game objects

16:33 Chousuke: do you really need a ref though? hmm.

16:33 I mean, perhaps you could reduce the state over the events with dispatch-event?

16:34 hiredman: yeah

16:34 alrex021: I know this is not really clojure q, but hope someone could help... I need to include Incanter lib jars into my emacs env. How should the (setq swank-clojure-extra-classpaths ..) look? Sorry but I'm new to clojure and lisp too :(

16:35 Chousuke: alrex021: (setq swank... '("file:///some/path/here/javalib.jar" "file://more/paths/")) ought to work

16:35 Fossi: later on the plan is to do it in parallel

16:36 hiredman: ,(doc pvreduce)

16:36 clojurebot: "/;nil; "

16:36 hiredman: clojurebot: curse you!

16:36 clojurebot: Excuse me?

16:36 Chousuke: but if future events depend on the earlier state, it's not very parallelisable :/

16:37 alrex021: Chousuke: thank you. Do I need to specify each required jar of the lib then of course? Or is it possible to say...all *.jar(s) in following dir?

16:37 liebke_: alrex021: technomancy created an incanter.el file, that I just added to the Incanter repository today, that might be helpful. It's in the bin/ directory

16:37 clojurebot: technomancy is to blame for all failures

16:37 Chousuke: Fossi: oh, and the (do ...) in changeColor is not needed. function bodies have an implicit do.

16:37 Fossi: ah, didn't know pvreduce yet

16:38 Chousuke: Fossi: pvreduce is very new; not in master yet.

16:38 Fossi: Chousuke: i know, i still like it for reminding me of side effects

16:38 i'll wrap each game object in it's own stm then

16:38 alrex021: liebke_: ahh, gr8 the author, what a gr8 community :) ...thanks

16:39 Chousuke: Fossi: hmm, yeah :)

16:39 Fossi: and i don't care much about order of things, or whether they fail at first, with games you do this calculation 60/s, so it doesn't have to be overly correct

16:39 liebke_: alrex021: good luck, I wish I could be more help, but I'm not an emacs user :)

16:40 Fossi: alrex021: (directory-files "~/projects/android/bannerwars/libs/" t ".*\.jar")

16:41 alrex021: liebke_: out of curiosity, what do you use for your clojure dev?

16:41 liebke_: I just use vim and a repl

16:42 emacs is the way to go though... if you can get it configured :)

16:42 lbj: liebke_: I'll recommend Emacs + SLIME to you, its a fairly effecient combo, and easily installable thanks to technomancy's clojure-mode

16:42 Fossi: it's infecting

16:42 gjohnson: k

16:43 Fossi: i begin to select text with ctrl space in firebird

16:43 liebke_: lbj: I know, but I've used vi/vim for too long to switch at this point

16:43 Fossi: and try to swap desktops with shift-left

16:43 liebke_: viper-mode? :)

16:44 gjohnson: how about vimclojure/gorilla

16:46 hiredman: http://technotales.wordpress.com/2007/10/03/like-slime-for-vim/

16:47 kotarak: *ieck* do yourself a favour and use vimclojure. (shameless advertisement)

16:47 the screen has problems.

16:48 It works but is tricky.

16:48 hiredman: *shrug*

16:50 if you ever open a file that contains function calls, not just function definitions, vimclojure will execute those function calls

16:50 I find that to be unacceptable

16:51 heck

16:51 kotarak: IMHO, you shouldn't do that. Providing an entry point brings flexibility and advantages when debugging.

16:51 hiredman: if you try and edit a .clj file that is not valid (not all the parens are closed, etc) vimclojure will puke

16:51 kotarak: I have an entry point, and I have a .clj file that contains a call to that entry point

16:52 so if I open up my scratch clojure file, vimclojure pukes

16:52 Chousuke: also, if the file contains (shell-out "rm -rf /") or something :P

16:52 kotarak: You can disable the dynamic part at any time and just use static vim with <C-n> and friends. Works also very well.

16:52 hiredman: yeah, I have vim clojure installed and use the static part

16:53 I never use gorilla

16:53 I use slime.vim works great, and it works for things besides clojure

16:58 Fossi: yay. it works again. now that i have that set up, i can start doing more business logic :)

17:11 gjohnson: okay, so I know this is a pretty remedial java question...

17:11 but how can I ask a threadpool for its size (number of threads)?

17:11 hiredman: ~jdoc java.util.concurrent.ThreadPool

17:11 gjohnson: I see that clojure.lang.Agent uses 2 + the number of cores detected by the runtime for the fixed threadpool size

17:11 hiredman: damn

17:12 gjohnson: ?

17:12 hiredman: ~jdoc java.util.concurrent.ThreadPoolExecutor

17:12 still no dice

17:12 http://java.sun.com/javase/6/docs/api/java/util/concurrent/ThreadPoolExecutor.html

17:12 there is a getPoolSize method

17:13 Chouser: show works great for this

17:13 gjohnson: ah, thanks.

17:13 Chouser: (show clojure.lang.Agent) --> discover pooledExecutor and soloExecutor

17:14 (show clojure.lang.Agent/pooledExecutor) --> discover getPoolSize and getMaximumPoolSize

17:14 ,(.getMaximumPoolSize clojure.lang.Agent/pooledExecutor)

17:14 clojurebot: 3

17:39 Drakeson: how can I make a memoized recursive function?

17:41 hiredman: ,(macroexpand '#'foo)

17:41 clojurebot: (var foo)

17:46 Drakeson: hiredman: that is (defn f1 [n] (if (zero? n) 0 (+ (#'f1 (dec n)) n))) (def f1 (memoize f1)) ?

17:46 hiredman: I am not sure

17:47 Chousuke: that would work, but it'll blow the stack if the recursion is too deep.

17:48 Chouser: that shouldn't be necessary

17:50 (defn f1 [n] (prn n) (when (> n 0) (f1 (dec n))))

17:50 (def f1 (memoize f1))

17:50 (f1 5) ; prints 5 to 0

17:50 (f1 10) ; prints 10 to 6

17:51 Drakeson: Chouser: thanks. That is an interesting way to test :)

17:51 * Drakeson takes note

17:59 alrex021_: I have been trying to pluggin this .el script into my .emacs for the past hour with no luck :( http://github.com/liebke/incanter/raw/fcbcdcc788916c204aca32e6b7810de4a6d0e6d2/bin/incanter.el

18:05 Could someone enlighten me on how one can configure "properly" full clojure support for emacs?

18:05 I am really struggling with this

18:05 hiredman: ~emacs

18:05 clojurebot: emacs is best configured for Clojure with instructions at http://technomancy.us/126

18:05 hiredman: *shrug*

18:15 alrex021_: hhm: Clojure install failed .. git clone git://github.com/kevinoneill/clojure.git when following http://technomancy.us/126 instructuins

18:16 durka42: hmm, those instructions are old, the git repository is under richhickey

18:16 http://github.com/richhickey/clojure/tree/master

18:17 technomancy: alrex021_: getting the latest version of clojure-mode will fix that problem.

18:18 alrex021_: hhmm so is ELPA option not gonna work?

18:19 I used ELPA to install latest clojure-mode .. then ran clojure-install ...that didn't work

18:19 so I take it I need to manual install

18:20 technomancy: alrex021_: clojure-mode 1.3 is in ELPA

18:20 that will do it

18:21 alrex021_: (I am extremely new to Emacs) ... do I have to restart after installing 1.3 via ELPA before I run clojure-install?

18:22 technomancy: you shouldn't have to, but if you're having problems you might try that.

18:22 alrex021_: also, I take it I don't have to add anything to my .emacs file after I install the clojure-mode

18:23 technomancy: no, but it will give you some code to add to your .emacs after M-x clojure-install finishes.

18:23 but it should be pretty clear from the instructions

18:26 alrex021_: ok I see, thx. I restarted and ran clojure-install...now its getting it from new git location

18:28 is there a way ti autoreload the .emacs file without restart of emacs?

18:28 off hand :)

18:28 technomancy: sure; open it and do M-x eval-buffer

18:29 alrex021_: thx

18:31 hhmm ok the clojure-install worked and it just gave me 1 liner to add to my .emacs and M-x slime doesn't work (slime not an option)

18:31 line it gave me is just (clojure-slime-config)

18:33 so I added (clojure-slime-config) to my .emacs and restarted and then ran: M-x slime

18:33 but no luck

18:38 rhickey: new new: added signature inference, overload detection: http://github.com/richhickey/clojure/commits/new

18:38 so now you don't need to hint except to disambiguate

18:38 hiredman: :O

18:38 :D

18:42 rhickey: ,(str (new [] (toString [] "hello")))

18:42 clojurebot: "sandbox$eval$obj__2040@945b95"

18:43 rhickey: hrm

18:46 hiredman: ,(str (new [] (toString [] "hello")))

18:46 clojurebot: java.security.AccessControlException: access denied (java.lang.RuntimePermission accessDeclaredMembers)

18:46 hiredman: whelp

18:46 it was a wild and crazy ride

18:46 rhickey: oh no

18:47 did clojurebot not like proxy either?

18:47 hiredman: yep

18:49 mebaran151_: is there a super type for Float and Double?

18:49 other than Number

18:49 rhickey: is permission java.lang.RuntimePermission "accessDeclaredMembers"; dangerous?

18:50 hiredman: ,(str (new [] (toString [] "hello")))

18:50 clojurebot: java.security.AccessControlException: access denied (java.lang.RuntimePermission accessDeclaredMembers)

18:51 hiredman: I dunno

18:51 ,(str (new [] (toString [] "hello")))

18:51 clojurebot: "hello"

18:51 hiredman: ,(ancestors Float)

18:51 clojurebot: #{java.lang.Object java.lang.Number java.io.Serializable java.lang.Comparable}

18:55 rhickey: cool

20:31 iBong: is there a (sugary) way to set flags like case insensitivity when using #regex literals?

20:44 sillycloud: yes

20:44 regexp : /pattern/i

20:45 or var rgx = new RegExp(); rgx.ignoreCase = true

20:47 iBong: thx

20:49 sillycloud: um

20:49 sorry

20:49 wrong channel

20:49 lol

20:49 iBong: er

20:49 sillycloud: my answer is obviously wrong

20:49 iBong: yeah that didnt work

20:49 sillycloud: >.<

20:49 iBong: I was like, cool, just like ruby

20:49 sillycloud: lols

20:50 iBong: the java way is super ugly

20:50 hoping clojure had a nice alternative, like not having to escape escapes

20:50 Raynes: sillycloud: Think you're in #scala? :p

20:50 sillycloud: worse

20:50 i'm currently in #flash

20:50 iBong: lol

20:50 sillycloud: helping the noobs

20:51 personally

20:51 i call zomg_var_to_lower_case() on all my string literals

20:51 iBong: is there a way to set case insensitive on #"regex" ?

20:55 arbscht: iBong: #"(?i)..."

20:58 iBong: thats it, nice, thank you

21:11 Raynes: ,(apply str (repeat 10 "(") (repeat 10 ")"))

21:11 clojurebot: "clojure.lang.LazySeq@b0c04bc1))))))))))"

21:11 hiredman: prn-str

21:12 Raynes: Cool.

21:29 gjohnson: hi there.

21:30 I had a lovely time reading through the code for pcalls, pmap, clojure.lang.Agent, Executors, and ThreadPoolExecutors, and I've just got one question at this point.

21:31 what is the rationale for the fixed threadpool's size being 2 greater than the number of cores?

21:32 anyone?

21:33 anyone alive out there?

21:33 rhickey: gjohnson: there isn't any special rationalization, other than it isn't worthwhile to create many more computation threads than you have procs

21:34 gjohnson: rhickey: indeed. I was wondering specifically why you chose to create 2 more than the number of cores.

21:34 rhickey: i.e. is there a performance benefit I'm missing from not having the same number or perhaps 1 or 3 more?

21:35 rhickey: gjohnson: no, it's still subject to change

21:35 gjohnson: rhickey: alright. until I finally took the time to poke into clojure.lang.Agent, I had been creating 2*cores agents and queueing up all my work evenly across them.

21:35 clojurebot: clojure is far closer to perfection then python

21:36 gjohnson: rhickey: that worked great on my 2 core machine (since it incidentally creates the same number of agents as your thread formula creates worker threads)

21:36 rhickey: but when I scaled up to more agents (say 4*cores), performance was degraded considerably.

21:37 rhickey: gjohnson: using send or send-off?

21:37 Chouser: ,(.getMaximumPoolSize clojure.lang.Agent/pooledExecutor)

21:37 clojurebot: 3

21:37 gjohnson: rhickey: just send, so they were all using the fixedThreadPool

21:38 rhickey: seemed like the extra agent overhead was just bogging down my processor without getting any advantage since there were no free threads.

21:38 rhickey: the number of agents shouldn't matter much then

21:39 gjohnson: rhickey: well, I need at least at many as there are threads in the thread pool, otherwise I'll have idle threads.

21:39 rhickey: it doesn't work that way, there are just jobs in the queues getting pulled off by threads, agents don't do anything themselves

21:39 gjohnson: rhickey: right, I understand that, but each agent can only have one thread running on it at a time.

21:39 rhickey: yes, I mean when more than numThreads

21:40 gjohnson: rhickey: so if there are less agents(work queues) than threads...well, you know.

21:40 rhickey: so 4 threads is 4 threads even if 100 agents

21:40 gjohnson: rhickey: right, but 4 threads is 3 threads if you only have 3 agents.

21:40 hiredman:

21:40 rhickey: but you said you added more agents and it slowed down

21:40 4 *cores

21:41 gjohnson: rhickey: yes, that was my observation. sorry, these are two different issues.

21:41 rhickey: what I noticed was that as I added more agents, my processors were being utilized less.

21:41 rhickey: do the agents do any io?

21:41 gjohnson: rhickey: usage was dropping maybe 10% or so on each one.

21:42 rhickey: no, they are doing CPU bounded work (agent-based simulation, no punning intended)

21:43 rhickey: perhaps I was just having a strange day at my machine. *shrugs*

21:43 rhickey: and was each job the same size?

21:44 gjohnson: rhickey: no, they were exploratory tree searches on variable-sized trees.

21:44 rhickey: but each time I ran the simulation, it was on the same set of trees.

21:44 rhickey: well, than some agents could be done and doing nothing and the work serializes on the others

21:44 gjohnson: rhickey: the data didn't change, just possibly the order that the agents got around to processing them.

21:44 rhickey: then

21:45 forkjoin was made for this

21:45 because it does work stealing

21:45 you should try par

21:45 gjohnson: rhickey: hmm...I see.

21:45 rhickey: the code pcalls (i.e. pmap) looked very promising for my application actually.

21:46 rhickey: agents are really for stateful asynchronous things, for threading you can just use futures, or ideally, try the par branch

21:46 gjohnson: rhickey: since I designed my algorithm to break all the work up into a sequence of fns, the futures via pcalls seems like the best idea.

21:46 rhickey: in the par branch you can put you job data in a vector and call pvmap, get work stealing etc for free, no coordination

21:47 gjohnson: rhickey: when I wrote this code, I don't believe futures had yet been added to the language spec.

21:47 rhickey: I've been at these algorithms for the past few years.

21:47 rhickey: gjohnson: right, I saw so many people using agents because they were a path to threads, but they weren't doing anything actor-like with them

21:48 hiredman: futures weren't present

21:48 gjohnson: indeed

21:48 * hiredman chortles

21:48 gjohnson: ack, I seem to have missed that.

21:49 rhickey: well, the truth of the matter is IMHO that once you're living and breathing clojure all day and night, it's really a thorn in my side to have to call out to the java Thread and Executors classes to get my concurrency off the ground.

21:50 rhickey: gjohnson: I'm not recommending that

21:50 gjohnson: rhickey: I had written my code with those initially, then switched it over to evenly queueing the tasks over a bunch of agents and passing it off to the threadpool transparently.

21:50 rhickey: I've been off in academic development land for quite awhile, and I just came back around and reread all the docs on the website, so futures were a new thing for me this week.

21:50 (yes, I know that's silly sounding, hiredman)

21:51 and I didn't find the parallel library terribly transparent in the website docs.

21:52 and the jsr166y link just led to a page that ultimately led me on to the ForkJoin API without a high-level explanation of what it was supposed to be doing.

21:52 rhickey: so I've been having a bit of trouble getting traction with it.

21:52 rhickey: any pointers on reading references to grok this stuff?

21:53 rhickey: http://github.com/richhickey/clojure/blob/26f5aed73c9cc2959beee0dd43a0d434fce83631/src/clj/clojure/par.clj

21:54 there is a par branch on github, try it

21:54 gotta run

21:54 gjohnson: alright, thanks

22:38 bpattison: I'm looking for a clojure function that tells me if a symbol is in a list -- I know it something obvious but I can't find it in the Clojure API or clojure.contrib

22:38 Chouser: are you sure you want a list and not a set?

22:39 ,(#{:a :b :c :d} :c)

22:39 clojurebot: :c

22:39 Chouser: ,(#{:a :b :c :d} :e)

22:39 clojurebot: nil

22:39 hiredman: (.contains '(:a :b :c) :a)

22:40 ,(.contains '(:a :b :c) :a)

22:40 clojurebot: true

22:40 bpattison: yep, that's perfect -- I'll use a set

22:40 hiredman: ,(.contains [:a :b :c] :a)

22:40 clojurebot: true

Logging service provided by n01se.net