2008-09-19
| 09: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? |
| 09:21 | rhickey | Wodin: what are the overloads? |
| 09:21 | Wodin | dcm is an instance of a Java class |
| 09:22 | rhickey | Wodin: or, if it's a class with javadoc online somewhere, an url for that |
| 09:22 | ozzilee | Wodin: I think you have too many parens. (. dcm getString 1234) |
| 09:22 | Wodin | getString(int tag), getString(int[] tagPath), getString(int[] tagPath, java.lang.String defVal), getString(int tag, java.lang.String defVal) |
| 09:23 | Wodin | http://www.dcm4che.org/docs/dcm4che2-apidocs/org/dcm4che2/data/BasicDicomObject.html |
| 09:24 | Wodin | ozzilee: Really? I thought you needed the parens when calling methods. Anyway, this works: (. dcm (get 1234)) |
| 09:24 | rhickey | Wodin: parens are optional but ok |
| 09:24 | Wodin | I'm using clojure 20080612 if that makes a difference. |
| 09:25 | Wodin | rhickey: OK |
| 09:26 | Wodin | 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. |
| 09:28 | Chouser | Wodin: what version of dcm4che are you using? |
| 09:28 | Wodin | dcm4che-2.0.15 |
| 09:29 | rhickey | Wodin: could you try: (. dcm getString (int 1234)) and (. #^BasicDicomObject dcm getString 1234) |
| 09:30 | Wodin | Same result for both. |
| 09:30 | Wodin | I had actually tried both of those (with the optional parens) before :) |
| 09:30 | rhickey | Wodin: you might want to try the 20080916 release: http://sourceforge.net/project/showfiles.php?group_id=137961 |
| 09:31 | rhickey | then we can chase in current Clojure |
| 09: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 :) |
| 09:31 | Wodin | OK |
| 09:32 | Chouser | (. (new org.dcm4che2.data.BasicDicomObject) getString 1234) <-- works for me (very recent version of clojure) |
| 09:32 | Chouser | It returns nil and doesn't throw an exception. |
| 09:32 | rhickey | Chouser: more recent than mine probably :) |
| 09:33 | Chouser | oh? |
| 09:33 | rhickey | just joking - I'll need to add support for ClojureScript |
| 09:33 | Wodin | Chouser: OK, that's encouraging. |
| 09:34 | Chouser | rhickey: :-) ah. Yeah, mine's all patched up. |
| 09:35 | Chouser | rhickey: don't apply the patch in clojure-contrib/clojurescript yet. It's not clean against HEAD anymore. |
| 09: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 |
| 09:35 | rhickey | javac doesn't do smap files, etc |
| 09:35 | Wodin | Excellent! It works now :) |
| 09:35 | Wodin | Thanks. |
| 09:37 | Chouser | rhickey: hm. C source has #line -- nothing like that for Java? |
| 09:39 | Chouser | 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. |
| 09:39 | rhickey | C# does too, nope nothing for Java |
| 09:39 | Chouser | rhickey: you're ok with the general approach of my Compiler patch making a bunch of stuff public? |
| 09: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 |
| 09:42 | Chouser | For fields that weren't already final, I made final accessor methods. |
| 09:51 | ozzilee | Why is it that this works: (.getDeclaredMethods (. Class forName "java.lang.System")) ... |
| 09:51 | ozzilee | But this fails: (.getDeclaredMethods java.lang.System) ? |
| 09:52 | ozzilee | Huh I think I just don't quite understand how Java does reflection. |
| 09:55 | Chouser | This come up so often. Lots of people trip on it. |
| 09:56 | Chouser | the System class has no getDeclaredMethods static method. |
| 09:56 | Chouser | the Class class has a getDeclaredMethods instance method. |
| 09:57 | Chouser | so you need to tell Clojure you want the latter and not the former. |
| 09:59 | ozzilee | ... |
| 09:59 | ozzilee | 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 | Chouser | 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 | rhickey | 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 | Chouser | 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 | ozzilee | The . is just screwing things up somehow when I use it directly on a symbol representing a class. |
| 10:11 | ozzilee | 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 | rhickey | when you say (. Classname member) that is static class scope |
| 10:19 | rhickey | 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 | rhickey | what static method of System? |
| 10:22 | rhickey | (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 | rhickey | 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 | rhickey | args must be in an array |
| 10:27 | Chouser | (.invoke (second (filter #(= (.getName %) "getenv") (.getMethods (identity System)))) System (to-array [])) |
| 10:27 | Chouser | oh! |
| 10:27 | Chouser | 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 | rhickey | I didn't do it the way you did, flowed line number up rather than source down |
| 11:53 | arohner | cool |
| 11:56 | arohner | 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 | Chouser | 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 | rhickey | 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 | abrooks | 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 | abrooks | 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 | rhickey | clojure.lang.AFn |
| 13:40 | Chouser | abrooks: (instance? clojure.lang.AFn {}) is also true |
| 13:40 | Chouser | abrooks: just so you know. |
| 13:40 | abrooks | Hrm. |
| 13:40 | rhickey | oh, yeah, scratch that |
| 13:40 | rhickey | no way then |
| 13:41 | rhickey | 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 | abrooks | 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 | rhickey | I'm stuck on JDK 1.5 OS X here... |
| 13:49 | abrooks | rhickey: It works for me with Sun JDK 1.6.0.05. |
| 13:49 | abrooks | (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 | Chouser | 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 | Chouser | yeah, I'm sure you're right. Can also gzip (which can be delivered to the browser directly). |
| 14:00 | Chouser | But that's also without any of the Collections. |
| 14:01 | Chouser | So it's probably a mostly meaningless number at this this point. |
| 14:01 | Chouser | So I should have brought it up. |
| 14:01 | Chouser | Sorry. |
| 14:01 | Chouser | :-) |
| 14:02 | Chouser | 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 | abrooks | Sheesh. |
| 14:09 | abrooks | 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:18 | Chouser | I've got XP SP2 in a vm here. |
| 14:22 | rhickey | works here in a vm on Server 2003, SP2 |
| 15:00 | rhickey | 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 | Chouser | Does apply only work on clojure-defined functions? |
| 15:09 | Chouser | 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 | Chouser | 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 | StartsWithK | 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 | StartsWithK | clojure i mean :) |
| 15:16 | Chouser | This has been working all week: http://n01se.net/paste/23H |
| 15:17 | Chouser | 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 | Chouser | that's with all the debugging stuff turned on |
| 15:21 | Chouser | 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 | StartsWithK | there will be no need to gzip that, i was thinking it will produce much larger code |
| 15:26 | StartsWithK | 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 | Chouser | 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 | Chouser | lazy varargs are tricky |
| 15:57 | rhickey | how so? |
| 15:58 | Chouser | JS supports varags by making with an "arguments" object that's like an array of all args passed in |
| 15:58 | Chouser | s/by making// |
| 16:00 | Chouser | a cljs function that takes [a b & r] is emitted so that r takes the tail part of "arguments" |
| 16:01 | Chouser | 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 | Chouser | 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 | Chouser | where c may be some kind of collection but is meant to be the 3rd actual argument to func. |
| 16:04 | Chouser | 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 | Chouser | 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 | Chouser | 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 | cemerick | Chouser: congrats on the first cut of clojurescript, BTW. Looks like quite the genie you're releasing :-) |
| 19:40 | cemerick | 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 |