#clojure log - Nov 05 2008

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

3:52 Lau_of_DK: Mornings gents

3:53 I have not yet tried Clojure web-capabilities for servlets and such, how much does one benefit from its concurrency features in that regard?

4:17 Or: Where would one benefit the most from applying Clojure on the web ?

4:26 cgrand: Lau_of_DK: Comet?

4:29 Lau_of_DK: I'll look into it

11:06 duck1123: does anyone ever from time to time get the error: Exception in thread "Read Loop Thread"

11:08 * Chouser smells emacs

11:10 duck1123: yes, this is in *inferior-lisp* when starting slime

11:10 it doesn't appear do cause any problems, it's just odd

11:11 Chouser: sorry, I haven't seen it

11:23 drewr: How do I prn from a different thread?

11:23 Does *out* get mucked with?

11:24 I'm trying to debug something that happens in a send. I guess I could throw and look at agent-errors.

11:27 wwmorgan: drewr: You could also do a (.println System/out "message")

11:30 kwatford: Hrm. That doesn't seem to work either.

11:30 Chouser: works for me: (send-off (agent nil) (prn :hi)

11:30 )

11:31 oh, scratch that.

11:32 proves nothing

11:32 ok, this works: (send-off (agent :hi) #(do (Thread/sleep 1000) (println %)))

11:35 drewr: Also, what's the diff between send and send-off?

11:36 Chouser: That doesn't print anything in my REPL.

11:36 Chouser: send uses a pool of threads sized based on your processor core count

11:37 so if you do a send on a function that blocks for IO or sleeps or something, you're gumming up the works.

11:37 send-off uses a thread from a pool that grows as needed, so it's ok to block.

11:38 drewr: odd. you're at a regular terminal REPL?

11:38 drewr: No :-)

11:38 That's probably why.

11:38 kwatford: doesn't seem to need the sleep in emacs. I was just forgetting to make the lambda take an argument.

11:39 Chouser: the sleep isn't required, just makes is clearer whats going on.

11:40 Without it I tend to see:

11:40 #<:hiclojure.lang.Agent@1a422f6

11:40 >

11:41 kwatford: ah, so that's why the raw repl gave me #<clojure.lang.Agent@71537:hi>

11:42 duck1123: hmm, I saw it as this: user=> #=(clojure.lang.Agent. "clojure.lang.Agent@36db4bcf")

11:42 user=> :hi

11:42

12:48 kwatford: hrm. Why does for not support use of :when and :while at the same time?

12:49 wwmorgan: kwatford: you can combine them into a :while, right?

12:49 AWizzArd: Moin

12:51 kwatford: contrived example: (for [x (iterate inc 0) :when (even? x) :while (< x 10)] x)

12:51 kotarak: AWizzArd: hi wizz

12:53 kwatford: Quick Hack(tm): (for [x (iterate inc 0) :when (even? x) y x :while (< y 10)] y) (not tested)

12:53 kwatford: IllegalArgumentException: Don't know how to create ISeq from Integer

12:54 kotarak: kwatford: oops, (for [x (iterate inc 0) :when (even? x) y [x] :while (< y 10)] y)

12:54 kwatford: OutOfMemoryError

12:56 kotarak: (for [x (iterate inc 0) :while (< x 10) y [x] :when (even? y)] y)

12:56 (0 2 4 6 8)

12:57 kwatford: There we go

13:04 sohail: you need the monstrous cl loop macro

13:04 :-D

13:05 kotarak: (take-while #(< % 10) (filter even? (iterate inc 0))) should also work, no?

13:05 kwatford: heh. It's been a while since I touched CL, but I do distinctly remember avoiding loop

13:06 kotarak: Yes, but I was more wondering "why doesn't this work?" than "how can I make it work?"

13:08 sohail: kwatford, it's an acquired (dis)taste

14:16 Lau_of_DK: Evening all!

14:16 And a good one at that :)

14:20 AWizzArd: hi Lau

15:10 Lau_of_DK: Is (remove) something new? I dont seem to have it

15:10 Chouser: newish, yeah

15:10 kotarak: relatively new

15:10 Chouser: Oct 16

15:11 Lau_of_DK: Hmm

15:11 I downloaded yesterday

15:11 Is the for SVN users only?

15:11 kotarak: I only use SVN.

15:11 Unless told otherwise by Rich.

15:11 Lau_of_DK: got the svn adress handy ?

15:12 kwatford: Top right corner of the webpage

15:12 kotarak: I use the hg mirror

15:12 duck1123: http://clojure.googlecode.com/svn/trunk/

15:13 there's a git mirror too if that's more your cup of tea

15:14 abrooks: duck1123: rhickey's still using SVN on sf.net for now.

15:15 duck1123: http://clojure.svn.sourceforge.net/viewvc/clojure/

15:16 Lau_of_DK: What is 'Cannot execute mojo resources' ?

15:16 duck1123: hmm... I guess you're right. I didn't bother to check the repository, I just knew there was a google code page

15:19 Lau_of_DK: As I remember, its just

15:19 svn co https://clojure.svn.sourceforge.net/svnroot/clojure clojure

15:19 mvn install

15:19 right ?

15:20 kotarak: I use ant

15:20 wwmorgan: ant

15:20 duck1123: just 'ant' is fine

15:20 Lau_of_DK: trying

15:20 Success

15:20 Thanks

15:21 * duck1123 needs to write a hook to run ant when svn updates clojure

15:22 danlarkin: duck1123: saw my clojure-json eh?

15:24 duck1123: yeah, I just started watching it. I planned on looking through it a little later

15:25 how hard would it be to translate clojure code into json?

15:25 it would be interesting to store code in couchdb

15:32 danlarkin: duck1123: I only have a clojure->json encoder in there right now

15:32 no json->clojure yet

15:33 duck1123: I just realized that github won't show a project if it's listed as a fork of another project

15:33 that's kind of annoying

15:34 danlarkin: translating code into json would be a little difficult, since functions are closures on their environment

15:34 I could use some help with my encoder, actually, it's a little slow for my taste

15:34 blackdog: what's the link to the clojure-json?

15:35 Chouser: json (the formal spec) doesn't allow functions either.

15:35 do you mean clojure code to javascript code?

15:35 danlarkin: Chouser: but it does allow lists :)

15:35 Chouser: yes it does. and maps and strings

15:36 oh... I see what you're saying.

15:36 danlarkin: blackdog: my clojure-json is located at http://github.com/danlarkin/clojure-json/tree/master

15:36 blackdog: thanks

15:37 Chouser: but how would you differentiate between vectors and lists?

15:38 duck1123: you would probably have to do it as (vec 1 2 3)

15:38 danlarkin: Chouser: Hmmmmm, not sure, maybe you wouldn't be able to

15:38 duck1123: ah yes, or that

15:39 duck1123: danlarkin: I'm curious, have you tried using org.json.JSONObject in clojure?

15:40 drewr: duck1123: I have, and it works OK.

15:40 It doesn't like j.u.Date very much.

15:42 danlarkin: duck1123: I have not. I wanted to be able to use clojure datastructures natively. Also I wanted to program a json interface my self as a learning exercise

15:43 duck1123: nothing wrong with that

15:44 danlarkin: I have a branch that uses Writer objects instead of strings, http://github.com/danlarkin/clojure-json/tree/writer

15:45 I have more on my TODO list regarding that, I need to add flushes so it doesn't blow up the heap

15:46 Chouser: danlarkin: might be a good candidate for multimethods

15:55 danlarkin: Chouser: what might be?

15:55 Chouser: your json printer

15:55 danlarkin: instead of a cond?

15:55 Chouser: yeah

15:56 danlarkin: wouldn't the multimethod dispatch have to do a cond anyway?

15:57 Chouser: looks like you could dispatch on class

15:57 then match Boolean, nil, String, Number, etc.

15:58 maybe it wouldn't be any better than what you've got.

16:01 danlarkin: I do have some performance inefficiencies, I don't think cond is one of them though

16:01 drewr: I thought there was an (into-map {:foo :bar}) now. What am I thinking of?

16:02 Don't Clojure's maps now implement j.u.HashMap?

16:02 Er, j.u.Map?

16:05 wwmorgan: drewr: yes. (into {} m) will take a java Map m and return a clojure Map

16:05 drewr: wwmorgan: I want to go the other way.

16:05 wwmorgan: You get that for free: (instance? java.util.Map {}) returns true

16:06 danlarkin: (instance? java.util.Map {}) == false for me

16:07 wwmorgan: I'm on svn head. I don't know when the change was added

16:08 duck1123: this came up yesterday

16:08 sometime early last month

16:09 danlarkin: I'm on an older checkout

16:09 haven't updated in a while

16:09 duck1123: Oct 6

16:11 danlarkin: the real problem is that apply walks the entire list it's passed, so I'm trying to rewrite everything not to use apply

16:11 duck1123: I thought apply was lazy

16:12 * duck1123 wishes the doc page linked to the actual source

16:13 danlarkin: http://blog.danlarkin.org/2008/11/apply-is-not-lazy/

16:13 kotarak: what would thatt help? Knowing the source is a smell. You should rely on the interface, not the implementation.

16:13 Lau_of_DK: Guys - Is there a non-nil?, a shorter version of (not (nil? x y z))

16:15 kotarak: some

16:15 or maybe every?

16:15 duck1123: kotarak: sometimes it helps to see the code to realize something that is not obvious from the documentation

16:15 kotarak: duck1123: and which might suddenly change.

16:16 Chouser: apply *is* lazy. str is not

16:17 Lau_of_DK: I hardly think 'apply' is as lazy as Chouser...

16:17 duck1123: the implementation might change, but the behavior generally does not.

16:17 Chouser: hardly anything is as lazy as me

16:18 duck1123: There needs to be Clojure shirts that say "I get more done when I'm lazy" or some such

16:18 Chouser: (defn first2 [a b & c] [a b])

16:19 kotarak: duck1123 haha, that's good. Maybe the Haskell guys have it already.

16:19 Chouser: (first2 1 2 3 4) -> [1 2]

16:19 If apply weren't lazy, this would never return: (apply first2 (iterate inc 1))

16:19 danlarkin: Chouser: apply is lazy? it looks like the spread helper function isn't though

16:20 Chouser: but in fact it returns [1 2]

16:20 kotarak: proof by counter-example

16:21 danlarkin: kotarak: you're right, it isn't a proof at all, but certainly good enough to prove me wrong

16:22 Chouser: spread is eager for the required args of the func you're applying

16:22 ...it has to be to match Java calling conventions, I think.

16:22 * duck1123 wants a lazy? fn

16:23 Chouser: but once the required args are met, the "rest" arg is left as a (possibly lazy) seq

16:24 but str needs to build a non-lazy string, so it has to consume all its args, whether the seq it's pulling from is lazy or not.

16:25 * danlarkin feels silly now

16:25 Chouser: and by the way, (apply str (range 10e6))

16:26 duck1123: hmm, in that case, would it be better to work with seqs of chars until you're ready for output?

16:26 Chouser: works fine for me, though (apply str (interpose "," (range 10e6))) does not

16:26 scottj: I often want to link to some identity, not copy it, in a sort of relational database manner. Like bob has his details and one of them is his hometown and that hometown has it's own details. Is the recommended method to make bob and his hometown each refs and then include the hometown ref in bob's map but not @ it until I want to follow the link?

16:28 Chouser: scottj: you want to be able to update the town's details and see those changes for all peoplewho live there?

16:30 scottj: Chouser: yeah

16:30 danlarkin: Chouser: so would applying to a lazy-consumer make sense? a function that takes [x & ys] and then uses (loop [more ys] ... (recur (rest ys)))? Or is that not really helping anything

16:30 scottj: Chouser: so either I use some primary key (name, id number) and look that up, or I link with a ref

16:30 Chouser: scottj: I think using a ref sounds reasonable.

16:31 you still might what the town refs all in one map (or set?) just for sanity.

16:32 danlarkin: generally using loop/recur like that means it's *not* a lazy-consumer.

16:33 danlarkin: Chouser: well I do have to make a string out of it, I just want to do it in a way that doesn't fill up the heap

16:33 Chouser: danlarkin: you brought that up the other day, and it's been troubling me.

16:34 I don't understand yet how (apply str (range 10e6))

16:34 can runs so quickly with so little memory impact

16:34 ...when using interpose in the kills my memory and CPU

16:35 duck1123: later folks

16:35 danlarkin: duck1123: bye bye

16:35 duck1123: driving home

16:35 danlarkin: Chouser: how long does (time (dorun [(apply str (range 10e6))])) take for you?

16:36 Chouser: "Elapsed time: 2425.807705 msecs"

16:36 danlarkin: and can you run (time (dorun [(apply str (range 10e7))])) or does it blow your heap

16:37 Chouser: OutOfMemoryError

16:37 danlarkin: I know what you mean though... interpose should be lazy

16:38 abrooks: It's really interleave that should be lazy, I think.

16:39 wwmorgan: Chouser: what about 20e6? You're probably just bumping up against your jvm heap size right?

16:39 danlarkin: is it possible that interleave isn't? looks like it uses map...

16:39 Chouser: danlarkin: with 10e7, you're trying to allocate something like a 1GB string

16:40 interleave claims to be lazy

16:40 abrooks: danlarkin: Right. interleave claimes to be lazy but it doesn't appear to be.

16:41 wwmorgan: even if interleave were lazy, we wouldn't see it because str is not lazy

16:41 Chouser: interpose acts lazy (take 10 (interpose "," (lazy-cat (range 10) (print "oops"))))

16:42 I don't think it's a laziness issue. It *might* have to do with something hanging onto the head of the seq, but I'm not sure.

16:43 wwmorgan: chouser: you mean why does (apply str (range 10e6)) work but (apply str (interpose "," (range 10e6))) blow up?

16:43 Chouser: wwmorgan: yes

16:44 wwmorgan: I think it's just that you're hitting your JVM's max heap size. The String you're trying to build is too big

16:44 Chouser: I don't think so.

16:45 without interpose I see very little extra memory usage, and I get a result in under 3 seconds.

16:45 with interpose, I would expect a string that's maybe 10% larger and shouldn't take more than maybe double the time to produce.

16:46 but with interpose I actually see my memory usage climb steadily and noresults for many seconds -- I've never let it finish as I fear for my other processes. :-)

16:47 wwmorgan: what happens when you do (apply str (interpose "," (range 10e5))) ?

16:48 Chouser: climbing memory usage, but a good result in about 3 seconds.

16:57 Lau_of_DK: Throw me a bone guys

16:57 (def f "test")

16:58 (re-seq #"test" seq) works

16:58 (re-seq #f seq) does not of course

16:58 but how do I make it work ?

16:59 kotarak: Is there something along the lines of (new java.util.Regex f)?

16:59 Chouser: (re-seq (java.util.regex.Pattern/compile f) seq)

16:59 Lau_of_DK: uuh fancy Chouser

16:59 How do you come up with all this stuff?

16:59 I mean... its almost like you and Rich have a secret book

16:59 Chouser: But don't use "seq" as a local name, since it's a clojure/seq (function)

16:59 Use the Source, Lau..K.

17:00 Lau_of_DK: Haha :)

17:00 Thanks alot guys

17:00 Chouser: and be careful building that string

17:03 danlarkin: are you going to update your blog, or am i going to have to leave a comment?

17:03 danlarkin: Chouser: you'll have to leave a comment, I'm at work now :)

17:03 Chouser: hmph.

17:03 Lau_of_DK: Chouser, , dont worry, I'll leave it totally in the hands of the competent user

17:07 danlarkin: so what's a lazy way to consume (interpose "," lst)? And If I do consume lazily and use a Writer and flush() it every time that'd cut down on the memory usage, wouldn't it?

17:08 Chouser: I haven't been able to figure out where the memory usage is coming from.

17:08 wwmorgan: I have an idea

17:09 str uses a StringBuilder. StringBuilder maintains an internal buffer. If you append to a StringBuilder over the buffer, it has to reallocate the whole thing

17:13 but simply consuming an interpose doesn't use up memory: (reduce (fn [_ _] 1) (interpose "," (range 10e6)))

17:28 lisppaste8: danlarkin pasted "consuming interpose" at http://paste.lisp.org/display/69773

17:31 wwmorgan: I don't think StringWriter's flush method clears its buffer

17:32 so you're still trying to retain (range 10e7) in memory

17:34 lisppaste8: wwmorgan annotated #69773 with "Doesn't consume memory" at http://paste.lisp.org/display/69773#1

17:40 danlarkin: wwmorgan: wow look at that, it doesn't. Hrmph, so how do I do this?

17:41 rhickey: danlarkin: do what?

17:41 * rhickey just arrived

17:41 AWizzArd: wb

17:41 danlarkin: election went smoothly I see

17:41 :)

17:41 wwmorgan: danlarkin: the function that you call needs consume a constant amount of memory no matter how many times you call it

17:42 danlarkin: rhickey: I'm writing a json encoder and (apply str (range 10e6)) is blowing the heap, but I need a way to consume huge lists like that

17:43 wwmorgan: if you're sending it over a network, then your function call wraps the network message-sending interface

17:49 rhickey: danlarkin: went well, yes, thanks - you can reduce with a StringBuilder

17:54 danlarkin: rhickey: is a stringbuilder efficient? doesn't it have to discard and double its buffer every time it fills up? or is that the best way to do it anyway

17:58 Chousuke: it's difficult to be more efficient when you need a dynamically growing chunk of contiguous memory

17:59 wwmorgan: danlarkin: do you need the whole String in memory at once?

18:00 danlarkin: wwmorgan: no

18:01 wwmorgan: what do you want to split the String up into?

18:03 danlarkin: wwmorgan: well like I said it's for a JSON library, so I assume it should be a Writer if it can't be a string, since then the user of the library can pass in a StringWriter or FileWriter or whatever

18:03 lisppaste8: wwmorgan annotated #69773 with "Splits the String up into Strings of 1024 characters each" at http://paste.lisp.org/display/69773#2

18:04 wwmorgan: oh, if the user is passing in the writer, then it's his own fault if he runs out of memory

18:05 lisppaste8: wwmorgan annotated #69773 with "slightly better" at http://paste.lisp.org/display/69773#3

18:08 wwmorgan: for your tests, just pass in a Writer that discards all its inputs

18:13 SnowBuddy: when I say (loop ... (recur)), I get an ArrayIndexOutOfBoundsException. can somebody explain why that happens and what exactly loop..recur is supposed to be doing behind the scenes? the documentation suggests that the arguments are optional (0 or more), so I figured I could do something like that

18:15 danlarkin: wwmorgan: partition throws out any remainder, (doseq cs (partition 3 (interpose "," (range 10))) (print (apply str cs))) prints 0,1,2,3,4,5,6,7,8,

18:15 wwmorgan: danlarkin: right, so you'd need your own partition that didn't do that

18:15 gnuvince_: SnowBuddy: try: (loop [] ... (recur))

18:16 lisppaste8: danlarkin annotated #69773 with "invocation target exception" at http://paste.lisp.org/display/69773#4

18:16 danlarkin: sorry if I'm being thick-skulled... maybe there's something I'm missing

18:23 lisppaste8: wwmorgan annotated #69773 with "try this" at http://paste.lisp.org/display/69773#5

18:26 SnowBuddy: gnuvince: that worked, thanks. looking at the documentation, i see now that the brackets are required :P

18:28 rhickey: danlarkin: if you are not accumulating in memory, then you must be either reducing your input or doing output as you go, so (dorun (map do-a-bit stuff-too-large-for-memory)) should work

18:41 gnuvince_: SnowBuddy: happy to help

18:43 SnowBuddy: now i just need to figure the rest of this stuff out ;p

18:44 Kerris4: I just bought the beta pdf of Stuart Holloway's Clojure book from Pragmatic Programmers :D

18:44 ohboyohboyohboy

18:44 SnowBuddy: how is it?

18:45 Kerris4: hang on, I've just downloaded it

18:45 :P

18:46 SnowBuddy: heh

18:47 Kerris4: I'm no Lisp/Clojure veteran but a brief scan shows that it is suitable for people coming from a Java background

18:47 I'd use this to get my classmates into Clojure as the school teaches Java

18:48 SnowBuddy: i'm coming from a C/C++/C# background, so i may pick that one up

18:49 Kerris4: Have you spent any amount of time with Lisp or Scheme or Haskell before, SnowBuddy?

18:50 SnowBuddy: I've played briefly with CL before discovering Clojure a few days ago

18:52 danlarkin: rhickey, wwmorgan: thanks for your help, I've gotta drive home now

18:53 oh, wwmorgan left :-/

18:54 Kerris4: I can't give a firm recommendation on the book as I'm of the opinion that you could learn a lot of Clojure for free by following Stuart Holloway's port of Practical Common Lisp to Clojure :P

18:55 SnowBuddy: i could learn a lot of Clojure by reading code and browsing the documentation, but that's tedious when just starting out ;)

18:56 Kerris4: true, and with his port you can just type in the code and watch things work

18:56 http://blog.thinkrelevance.com/2008/9/16/pcl-clojure

18:56 if you didn't know already, Practical Common Lisp is free

18:56 http://gigamonkeys.com/book/

18:56 SnowBuddy: yea, i read a bit of it when I was looking at CL

19:04 kwatford: The book is missing several chapters as of yet, so I wouldn't hand it to someone wanting to learn just yet. But should probably be much better when it's actually done ;)

19:24 hsuh: whois jao

21:29 dakrone: I apologize in advance if this is a silly question, but can someone tell me why http://paste.lisp.org/display/69785 hangs when I run it from the command line?

21:29 I don't quite understand how to use agents correctly

21:38 Chouser: dakrone: those files exist?

21:39 dakrone: Chouser, yep

21:39 I get:

21:39 ? clj ciosim.clj

21:39 file3

21:39 Reading file: file1

21:39 Read bytes: 24

21:39 and then it hangs until I ctrl+c

21:44 Chouser: dakrone: It doesn't hang for me, I get: java.lang.Exception: Agent has errors (NO_SOURCE_FILE:0)

21:45 rhickey: Chouser: did you consider extending RestFn for callable defstruct ?

21:45 dakrone: rhickey, huhwhatnow?

21:46 Chouser: rhickey: heh. nope. I guess I'm out of my depth?

21:46 * Chouser looks at RestFn

21:46 dakrone: Chouser, just tried it in the REPL instead of from the file, it doesn't hang, is there a reason why it would hang when being run as a script instead?

21:46 rhickey: might make it easier - I didn't look too closely yet

21:48 Chouser: rhickey: if the goal is acceptible, I'll look at it.

21:49 dakrone: you were trying it with Script?

21:50 dakrone: Chouser, yep

21:52 are there any good super-basic threading tutorials for Clojure, other than the ant simulation?

21:58 rhickey: Chouser: yes, sure

22:06 Chouser: dakrone: I don't know why it's hanging.

22:07 dakrone: Chouser, okay, I appreciate you trying though :)

22:07 thanks

22:09 Chouser: dakrone: are you getting the agent errors when you runin the repl?

22:09 dakrone: Chouser, nope, no errors are showing up

22:10 latest version of Clojure from svn also

22:13 Chouser: well, I can replicate it here, but I don't know of and can't see any difference between Script and Repl that would cause this.

22:14 dakrone: hmm, that's very strange

22:20 hmmm....interesting

22:21 OSX 10.5, java 5, 32-bit gives no errors, java 6, 64-bit gives the Agent has errors error

22:41 bradbev: is there an easy way to map from a java method name like user.baz__3364 to a clojure symbol user/baz? Obviously I could drop the __3364, but I'd prefer the correct way to do it rather than a heuristic.

22:44 sohail: Chouser, if clojure was not a lisp-1 you could have done it quite easily ;-)

22:46 Chouser: sohail: hm? what?

22:46 bradbev: you only have the method name, not the actual var or fn?

22:46 sohail: making struct callable?

22:47 Chouser: sohail: oh.

22:47 it wasn't too hard, but I leaned heavily on vim macros. RestFn is probably a better way.

22:47 bradbev: Chouser: I have the stack trace that comes out at the slime repl. I figured I could make a little patch that would look up the stack entries & try to make them look like native Clojure names

22:49 Chouser: bradbev: can you inspect the stack object itself, maybe get to the class objects, rather than munging the printed text?

22:51 bradbev: Chouser: I think so, but StackTraceElement.getClassName for (defn foo) gives me user.foo__1234. So I guess that Clojure gensyms up the internal java name. That way you can redefine functions & keep the old def in a closure

23:08 kwatford: brabev: doesn't look to me like the function object/class keeps that information. I guess you could parse the name and then check that the guessed symbol holds the function. Unless it didn't come from defn.

23:10 bradbev: kwatford: yeah, I can't see it either. Oh well, heuristic it is.

23:19 sohail: Chouser, couldn't you also do the "make struct callable" by making struct a macro? (defstruct foo ...) -> (def foo-struct ...) (defn foo [...] ...)

23:19 and then the make-struct macro appends -struct to the symbol. anyway just a thought :-)

23:23 Chouser: Or just http://tinyurl.com/5r42n6

Logging service provided by n01se.net