#clojure log - Sep 19 2008

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

9:20 Wodin: Hi. I'm trying out Clojure. I have an object with overloaded getString methods, and calling (. dcm (getString 1234)) gives me an IllegalArgumentException and says "No matching method found". The equivalent call works fine using Jython, and one of the getString methods does expect an Integer. Another method that also expects an Integer works as expected. How do I get it to work?

9:21 rhickey: Wodin: what are the overloads?

9:21 Wodin: dcm is an instance of a Java class

9:22 rhickey: Wodin: or, if it's a class with javadoc online somewhere, an url for that

9:22 ozzilee: Wodin: I think you have too many parens. (. dcm getString 1234)

9:22 Wodin: getString(int tag), getString(int[] tagPath), getString(int[] tagPath, java.lang.String defVal), getString(int tag, java.lang.String defVal)

9:23 http://www.dcm4che.org/docs/dcm4che2-apidocs/org/dcm4che2/data/BasicDicomObject.html

9:24 ozzilee: Really? I thought you needed the parens when calling methods. Anyway, this works: (. dcm (get 1234))

9:24 rhickey: Wodin: parens are optional but ok

9:24 Wodin: I'm using clojure 20080612 if that makes a difference.

9:25 rhickey: OK

9:26 I saw some mention of using type hints to avoid the reflection, but wasn't sure how to use them, and what I tried didn't work either.

9:28 Chouser: Wodin: what version of dcm4che are you using?

9:28 Wodin: dcm4che-2.0.15

9:29 rhickey: Wodin: could you try: (. dcm getString (int 1234)) and (. #^BasicDicomObject dcm getString 1234)

9:30 Wodin: Same result for both.

9:30 I had actually tried both of those (with the optional parens) before :)

9:30 rhickey: Wodin: you might want to try the 20080916 release: http://sourceforge.net/project/showfiles.php?group_id=137961

9:31 then we can chase in current Clojure

9:31 Wodin: Ah, didn't know there was a version released in the last couple of days. Sorry, would have tried it if I had realised :)

9:31 OK

9:32 Chouser: (. (new org.dcm4che2.data.BasicDicomObject) getString 1234) <-- works for me (very recent version of clojure)

9:32 It returns nil and doesn't throw an exception.

9:32 rhickey: Chouser: more recent than mine probably :)

9:33 Chouser: oh?

9:33 rhickey: just joking - I'll need to add support for ClojureScript

9:33 Wodin: Chouser: OK, that's encouraging.

9:34 Chouser: rhickey: :-) ah. Yeah, mine's all patched up.

9:35 rhickey: don't apply the patch in clojure-contrib/clojurescript yet. It's not clean against HEAD anymore.

9:35 rhickey: I'm still thinking about AOT compilation to Java. In going through it, I remember one of the shortcomings of compiling to source was getting debug info into the resulting bytecode

9:35 javac doesn't do smap files, etc

9:35 Wodin: Excellent! It works now :)

9:35 Thanks.

9:37 Chouser: rhickey: hm. C source has #line -- nothing like that for Java?

9:39 there's nothing like that for JavaScript. As of last night I'm emitting internal names for all functions, which I'm only using so JS debuggers can print stack traces.

9:39 rhickey: C# does too, nope nothing for Java

9:39 Chouser: rhickey: you're ok with the general approach of my Compiler patch making a bunch of stuff public?

9:41 rhickey: Chouser: yeah, especially if it can all be made final - not sure it can. Just begs the question of JavaDoc for the RT lib, in order to distinguish ok-to-use public from not-ok-public

9:42 Chouser: For fields that weren't already final, I made final accessor methods.

9:51 ozzilee: Why is it that this works: (.getDeclaredMethods (. Class forName "java.lang.System")) ...

9:51 But this fails: (.getDeclaredMethods java.lang.System) ?

9:52 Huh I think I just don't quite understand how Java does reflection.

9:55 Chouser: This come up so often. Lots of people trip on it.

9:56 the System class has no getDeclaredMethods static method.

9:56 the Class class has a getDeclaredMethods instance method.

9:57 so you need to tell Clojure you want the latter and not the former.

9:59 ozzilee: ...

9:59 So (= java.lang.System (. Class forName "java.lang.System")) is technically false, right?

10:02 Chouser: no, Clojure just has special rules for the . form to give you access to static fields and methods of a class.

10:03 anytime you try to "pass" a class around, Clojure has you pass the Class instance, which has methods like getDeclaredMethods.

10:03 rhickey: ozzilee: if you can't say X.y() in Java you can't say (. X y) or (.y X) in Clojure, and you can't say System.getDeclaredMethods() in Java

10:04 Chouser: Your = test is comparing two reverences to the same Class instance.

10:05 ozzilee: Chouser: Well that's what I thought, except one of them lets me say getDeclaredMethods and they other doesn't. Obviously there's a difference, I just don't see what it is.

10:05 rhickey: . processes it's first argument specially, not normal evaluation

10:05 its

10:06 Chouser: ozzilee: out of curiosity, is this code that will end up in a program, or are you just exploring the class definition at the REPL?

10:07 ozzilee: Chouser: Just exploring.

10:08 Chouser: Yeah, I think that's almost exclusively when this issue comes up. That's certainly where I tripped on it the first couple times.

10:09 In "real" code, you either mean a static on a specific class, or you're doing reflection on a Class instance, each of which work as expected.

10:09 rhickey: just use: (.getDeclaredMethods (identity System))

10:09 Chouser: It's only surprising when you're trying to do reflection on a literal class name.

10:10 ozzilee: Ah, ok, if I make a function like (print-methods [x] ...), and call (.getDeclaredMethods x), it works.

10:11 The . is just screwing things up somehow when I use it directly on a symbol representing a class.

10:11 Wait. Nevermind. I get it.

10:14 lisppaste8: Chouser pasted "reflection at the repl" at http://paste.lisp.org/display/67122

10:15 ozzilee: Chouser: Nice.

10:15 Chouser: hm, I haven't looked at those in a while. A couple helper functions could get rid of a lot of code.

10:15 ozzilee: So, out of curiosity, can I call a static method on (identity System) ?

10:18 rhickey: ozzilee: you need to distinguish between class scope and class instances. Java has both

10:19 when you say (. Classname member) that is static class scope

10:19 when you say Classname anywhere else, that is a class instance

10:20 Chouser: ozzilee: yeah, using the reflection API -- get the Method object and call its invoke method

10:20 rhickey: huh?

10:21 what static method of System?

10:22 (System/getProperties)

10:23 Chouser: that's a direct static call. I think ozzilee is asking how to call a static method when given a Class instance

10:23 rhickey: (identity System) is an instance of the class Class. You can call its static methods like: (Class/forName ...)

10:24 oh, yuck. Makes sure that's really what you want to do

10:25 Chouser: right, I can't imagine why you'd want to, but it should be possible.

10:25 rhickey: if so, Clojure's Reflector makes reflection easy: (Reflector/invokeStaticMethod aclass "methodName" args...)

10:26 args must be in an array

10:27 Chouser: (.invoke (second (filter #(= (.getName %) "getenv") (.getMethods (identity System)))) System (to-array []))

10:27 oh!

10:27 Yeah, Reflector looks easier. :-)

10:28 rhickey: Clojure needs a FAQ...

10:29 Chouser: yet another vector of docs :-/

10:38 ozzilee: rhickey: Yeah, there's no sane reason to do that, just curious :-)

11:06 Chouser: aw, I was hoping PersistentStructMap was Comparable, based on the order of its fields.

11:32 lisppaste8: Chouser annotated #67122 with "better reflection at the repl" at http://paste.lisp.org/display/67122#1

11:49 arohner: rhickey: did you see my patch for the reader exception message?

11:49 rhickey: arohner: did you see my fix for reader exceptions :)

11:49 I didn't do it the way you did, flowed line number up rather than source down

11:53 arohner: cool

11:56 do you have any hints for things I should consider? I'm trying to get attuned to how you like to do things

13:18 Chouser: rhickey: clojure-contrib/clojurescript/clojurescript-compiler.patch is now up to date.

13:18 I don't have any reason to think it will require any more changes.

13:23 rhickey: arohner: nothing specific - sometimes it is just easier for me to fix something than analyze a patch

13:24 I guess the source for Clojure might indicate something, once you get your head around it. Also, Clojure itself betrays my preferences

13:25 arohner: ok. I'll keep reading the source :-)

13:35 abrooks: Is there a way to differentiate between an actual fn and things such as keywords, maps, etc. which just implement iFn?

13:36 rhickey: abrooks: for what purpose?

13:36 abrooks: I want to differentiate between keyword arguments and fn created arguments.

13:36 I could use class.

13:36 Chouser: oh, I though a final method in Java was like a C++ function that returns a const ref. It's not.

13:36 abrooks: Which would tell me that the keyword is a keyword, I suppose.

13:37 rhickey: There's a keyword? predicate

13:37 abrooks: And that would work in this case where I'm only trying to tell keywords from other iFns but it doesn't work in all cases.

13:37 i.e. where I actually want to know if something was created with fn or kin.

13:38 arohner: kin?

13:38 abrooks: Things relating to fn (defn, #() etc.)

13:38 arohner: oh, right

13:38 rhickey: right now, all fns created that way derive from AFn, but that's an implementation detail

13:39 abrooks: rhickey: Hm. Okay.

13:39 rhickey: so (instance? AFn x) will work for you

13:40 abrooks: AFn isn't defined.

13:40 rhickey: abrooks: I still don't see your use case though

13:40 clojure.lang.AFn

13:40 Chouser: abrooks: (instance? clojure.lang.AFn {}) is also true

13:40 abrooks: just so you know.

13:40 abrooks: Hrm.

13:40 rhickey: oh, yeah, scratch that

13:40 no way then

13:41 you'll need to give me a good use case

13:41 Chouser: abrooks: are you sure you want to do this? Lots of times it's nice to be able to drop a map, set, or keyword in where something is expecting a fn

13:42 abrooks: rhickey: I'm creating an enumeration mechanism (for EXTERNAL reasons -- I'd just use keywords otherwise...). I want keywords, strings and numbers to be the enumeration keys and fns to be supplied to change the enumeration values. In this case, values matter -- X11 protocol.

13:42 rhickey: Clojure's abstractions are oriented towards providing overlapping views of the world, not partitioning it

13:44 abrooks: rhickey: I think it's good not to have to care in dynamic languages but to be able to care when you decide you need to.

13:44 rhickey: abrooks: yes, I'm not arguing against it, just explaining why they aren't so much help here

13:45 abrooks: The generic interfaces should work everywhere -- that's why we like duck-typing. It is important for, introspection reasons, to be albe to tell the different whatsits apart.

13:45 rhickey: I'm not complaining, BTW. :)

13:46 rhickey: Can anyone reproduce Cliff's problem: http://groups.google.com/group/clojure/msg/bdc33bec4a01370f

13:47 I'm stuck on JDK 1.5 OS X here...

13:49 abrooks: rhickey: It works for me with Sun JDK

13:49 (Linux)

13:49 rhickey: abrooks: you'll have to do a negative test, i.e. IFn and not keyword? and not collection? -> probably real function, maybe

13:50 abrooks: rhickey: Ah. That could work. Thanks.

13:50 rhickey: on my side, I'd have to add a marker interface... not sure I want to do that

13:50 abrooks: Understood.

13:56 Chouser: boot.js (generated from boot.clj) is currently about 143KB

13:57 rhickey: is that good or bad?

13:57 Chouser: that's without the optional debugging output.

13:58 well, it's not fantastic.

13:59 rhickey: I imagine with identifier compression that could become much smaller?

13:59 Chouser: jQuery is a popular lib that augments JS and browser DOM stuff -- 94KB

14:00 yeah, I'm sure you're right. Can also gzip (which can be delivered to the browser directly).

14:00 But that's also without any of the Collections.

14:01 So it's probably a mostly meaningless number at this this point.

14:01 So I should have brought it up.

14:01 Sorry.

14:01 :-)

14:02 hm, there are also still things in boot.js that are completely useless.

14:07 abrooks: Is this the best I can do for (collection? ): (ancestors (class {}) java.util.Collection)

14:07 arohner: rhickey: do you need the jdk or jre to run an existing clojure.jar?

14:08 rhickey: abrooks: sorry, it's coll?

14:08 abrooks: Oh, shoot. I missed that.

14:08 rhickey: arohner: yes

14:08 abrooks: I've used that before.

14:08 Sheesh.

14:09 Chouser: We should group all of the predicates together in the docs. :)

14:09 Chouser: returns boolean

14:09 rhickey: find-doc takes a regex string

14:10 arohner: rhickey: but you can run with only a jre?

14:10 Chouser: arohner: right, no javac required

14:10 rhickey: arohner: should work, yes

14:10 arohner: I was thinking about cliff's problem. I have a windows box that I only use for outlook

14:12 rhickey: someone reproduced on XP SP3

14:12 * rhickey hates windows

14:18 Chouser: I've got XP SP2 in a vm here.

14:20 * Chouser installs java

14:22 rhickey: works here in a vm on Server 2003, SP2

15:00 Chouser: ClojureScript compiler patch applied (rev 1035)

15:04 Chouser: rhickey: cool, thanks.

15:05 rhickey: thank you for ClojureScript!

15:06 Chouser: heh, yeah, we'll see...

15:09 Does apply only work on clojure-defined functions?

15:09 not Java methods?

15:09 rhickey: right

15:09 StartsWithK: Chouser: what parts of ClojureScript work now?

15:11 Chouser: StartsWithK: JS is emitted for any valid Clojure code (I think), so I'm working on the runtime support now.

15:11 Vars, lazy-cons, various bits and pieces of boot.clj work.

15:12 StartsWithK: you will rewrite all clojure data structures in js yourself?

15:12 Chouser: yes, although I'm actually hoping to write them in Clojure. :-) We'll see.

15:13 StartsWithK: so, there is a chance clojure will become selfhosting?

15:14 Chouser: I've written PersistentVector, ArraySeq, LazyCons, and bits of some other stuff in JS.

15:14 StartsWithK: can it access browsers dom?

15:14 or any other js inside the browser?

15:14 rhickey: StartsWithK: not yet, ClojureScript uses the Clojure compiler's analysis phase

15:15 Chouser: yes, access to browser JS and Dom feels like Java access.

15:16 StartsWithK: rhickey: any plans in making it selfhosted one day?

15:16 clojure i mean :)

15:16 Chouser: This has been working all week: http://n01se.net/paste/23H

15:17 that demonstrates DOM access, lazy seqs, macro expansion, and at least the skeleton of destructuring.

15:17 rhickey: StartsWithK: I don't know, I'm most interested in writing new things, e.g. a Java emitting phase for AOT compilation. Rewriting the analysis phase is less interesting, since it is pretty complex and works already

15:18 StartsWithK: Chouser: have a link to generated js maybe? just to see how large output is

15:18 drewr: Chouser: I didn't know you where working on this. Excellent!

15:19 Chouser: http://n01se.net/paste/Qhn

15:20 that's with all the debugging stuff turned on

15:21 Without that it's slightly more compact: http://n01se.net/paste/DlE1

15:24 StartsWithK: wow

15:24 Chouser: sorry, did I scare you? :-)

15:24 StartsWithK: no, it looks great

15:25 there will be no need to gzip that, i was thinking it will produce much larger code

15:26 i think you'll have your first customer next week :)

15:26 Chouser: well, that's the direct output of just the "application" code pasted earlier. It will still depend on the .js versions of boot.clj and the data structures.

15:27 hm, now *you're* scaring *me*.

15:28 StartsWithK: i have very simple page, and i only serve json to very small js script, this looks like it can replace it.

15:30 Chouser: I haven't made any attempt to package this up for real use, but everything you need is in clojure-contrib if you're feeling adventurous.

15:56 lazy varargs are tricky

15:57 rhickey: how so?

15:58 * abrooks pays attention -- when rhickey says, "how so?" we learn things...

15:58 Chouser: JS supports varags by making with an "arguments" object that's like an array of all args passed in

15:58 s/by making//

16:00 a cljs function that takes [a b & r] is emitted so that r takes the tail part of "arguments"

16:01 cljs code calling such a function could put all arguments in a JS Array and do func.apply( argarray ). This would work but be eager.

16:02 I'd want the calling code to instead call func( a, b, lazy_rest_part ), but can func tell the difference between that and func( a, b, c )?

16:02 where c may be some kind of collection but is meant to be the 3rd actual argument to func.

16:04 I need some way to signal to the called function that the caller has packages up "rest" for it already, but of course I have no function overloading of any kind in JS.

16:05 oh! I have an extra "this" arg that I'll never use in a clojure-defined function. Hm...

16:05 rhickey: yeah, that's a problem. Clojure uses AFn, RestFn and applyTo to handle this stuff

16:05 Chouser: I've been looking at AFn and applyTo... need to check RestFn

16:06 hm. I think "this" might just do it...

16:13 ozzilee: Chouser: I won't pretend to understand what you're doing, but you do know that you can set arbitrary properties on the function itself, and access them with this.callee, right?

16:13 Chouser: ozzilee: yessir! wait, arguments.callee, right?

16:14 ozzilee: Chouser: Yeah, arguments.callee, you're right.

16:14 Chouser: yep, using that already. ...and indeed will probably be using it more for this problem.

16:15 ozzilee: Alrighty. FIgured you knew, just making sure.

16:15 Chouser: yep, thanks.

19:29 cemerick: it's funny, in the course of two months, I've gone from dismissing resolution of cyclic dependencies between clojure and java outright, to thinking that it would be really handy, to really, really needing a solution. :-/

19:30 Chouser: do you really mean cyclic, or just interleving?

19:34 cemerick: Chouser: hrm, probably interleaving

19:35 Chouser: congrats on the first cut of clojurescript, BTW. Looks like quite the genie you're releasing :-)

19:40 looks like at least one approach in the groovy world is generating stubs to let javac finish, and then building the real groovy classes afterwards: http://docs.codehaus.org/display/GROOVY/GMaven+-+Building+Groovy+Projects#GMaven-BuildingGroovyProjects-StubGeneration

Logging service provided by n01se.net