#clojure logs

2008-09-19

09:20WodinHi. 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:21rhickeyWodin: what are the overloads?
09:21Wodindcm is an instance of a Java class
09:22rhickeyWodin: or, if it's a class with javadoc online somewhere, an url for that
09:22ozzileeWodin: I think you have too many parens. (. dcm getString 1234)
09:22WodingetString(int tag), getString(int[] tagPath), getString(int[] tagPath, java.lang.String defVal), getString(int tag, java.lang.String defVal)
09:23Wodinhttp://www.dcm4che.org/docs/dcm4che2-apidocs/org/dcm4che2/data/BasicDicomObject.html
09:24Wodinozzilee: Really? I thought you needed the parens when calling methods. Anyway, this works: (. dcm (get 1234))
09:24rhickeyWodin: parens are optional but ok
09:24WodinI'm using clojure 20080612 if that makes a difference.
09:25Wodinrhickey: OK
09:26WodinI 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:28ChouserWodin: what version of dcm4che are you using?
09:28Wodindcm4che-2.0.15
09:29rhickeyWodin: could you try: (. dcm getString (int 1234)) and (. #^BasicDicomObject dcm getString 1234)
09:30WodinSame result for both.
09:30WodinI had actually tried both of those (with the optional parens) before :)
09:30rhickeyWodin: you might want to try the 20080916 release: http://sourceforge.net/project/showfiles.php?group_id=137961
09:31rhickeythen we can chase in current Clojure
09:31WodinAh, didn't know there was a version released in the last couple of days. Sorry, would have tried it if I had realised :)
09:31WodinOK
09:32Chouser(. (new org.dcm4che2.data.BasicDicomObject) getString 1234) <-- works for me (very recent version of clojure)
09:32ChouserIt returns nil and doesn't throw an exception.
09:32rhickeyChouser: more recent than mine probably :)
09:33Chouseroh?
09:33rhickeyjust joking - I'll need to add support for ClojureScript
09:33WodinChouser: OK, that's encouraging.
09:34Chouserrhickey: :-) ah. Yeah, mine's all patched up.
09:35Chouserrhickey: don't apply the patch in clojure-contrib/clojurescript yet. It's not clean against HEAD anymore.
09:35rhickeyI'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:35rhickeyjavac doesn't do smap files, etc
09:35WodinExcellent! It works now :)
09:35WodinThanks.
09:37Chouserrhickey: hm. C source has #line -- nothing like that for Java?
09:39Chouserthere'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:39rhickeyC# does too, nope nothing for Java
09:39Chouserrhickey: you're ok with the general approach of my Compiler patch making a bunch of stuff public?
09:41rhickeyChouser: 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:42ChouserFor fields that weren't already final, I made final accessor methods.
09:51ozzileeWhy is it that this works: (.getDeclaredMethods (. Class forName "java.lang.System")) ...
09:51ozzileeBut this fails: (.getDeclaredMethods java.lang.System) ?
09:52ozzileeHuh I think I just don't quite understand how Java does reflection.
09:55ChouserThis come up so often. Lots of people trip on it.
09:56Chouserthe System class has no getDeclaredMethods static method.
09:56Chouserthe Class class has a getDeclaredMethods instance method.
09:57Chouserso you need to tell Clojure you want the latter and not the former.
09:59ozzilee...
09:59ozzileeSo (= java.lang.System (. Class forName "java.lang.System")) is technically false, right?
10:02Chouserno, Clojure just has special rules for the . form to give you access to static fields and methods of a class.
10:03Chouseranytime you try to "pass" a class around, Clojure has you pass the Class instance, which has methods like getDeclaredMethods.
10:03rhickeyozzilee: 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:04ChouserYour = test is comparing two reverences to the same Class instance.
10:05ozzileeChouser: 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:05rhickey. processes it's first argument specially, not normal evaluation
10:05rhickeyits
10:06Chouserozzilee: 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:07ozzileeChouser: Just exploring.
10:08ChouserYeah, I think that's almost exclusively when this issue comes up. That's certainly where I tripped on it the first couple times.
10:09ChouserIn "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:09rhickeyjust use: (.getDeclaredMethods (identity System))
10:09ChouserIt's only surprising when you're trying to do reflection on a literal class name.
10:10ozzileeAh, ok, if I make a function like (print-methods [x] ...), and call (.getDeclaredMethods x), it works.
10:11ozzileeThe . is just screwing things up somehow when I use it directly on a symbol representing a class.
10:11ozzileeWait. Nevermind. I get it.
10:14lisppaste8Chouser pasted "reflection at the repl" at http://paste.lisp.org/display/67122
10:15ozzileeChouser: Nice.
10:15Chouserhm, I haven't looked at those in a while. A couple helper functions could get rid of a lot of code.
10:15ozzileeSo, out of curiosity, can I call a static method on (identity System) ?
10:18rhickeyozzilee: you need to distinguish between class scope and class instances. Java has both
10:19rhickeywhen you say (. Classname member) that is static class scope
10:19rhickeywhen you say Classname anywhere else, that is a class instance
10:20Chouserozzilee: yeah, using the reflection API -- get the Method object and call its invoke method
10:20rhickeyhuh?
10:21rhickeywhat static method of System?
10:22rhickey(System/getProperties)
10:23Chouserthat's a direct static call. I think ozzilee is asking how to call a static method when given a Class instance
10:23rhickey(identity System) is an instance of the class Class. You can call its static methods like: (Class/forName ...)
10:24rhickeyoh, yuck. Makes sure that's really what you want to do
10:25Chouserright, I can't imagine why you'd want to, but it should be possible.
10:25rhickeyif so, Clojure's Reflector makes reflection easy: (Reflector/invokeStaticMethod aclass "methodName" args...)
10:26rhickeyargs must be in an array
10:27Chouser(.invoke (second (filter #(= (.getName %) "getenv") (.getMethods (identity System)))) System (to-array []))
10:27Chouseroh!
10:27ChouserYeah, Reflector looks easier. :-)
10:28rhickeyClojure needs a FAQ...
10:29Chouseryet another vector of docs :-/
10:38ozzileerhickey: Yeah, there's no sane reason to do that, just curious :-)
11:06Chouseraw, I was hoping PersistentStructMap was Comparable, based on the order of its fields.
11:32lisppaste8Chouser annotated #67122 with "better reflection at the repl" at http://paste.lisp.org/display/67122#1
11:49arohnerrhickey: did you see my patch for the reader exception message?
11:49rhickeyarohner: did you see my fix for reader exceptions :)
11:49rhickeyI didn't do it the way you did, flowed line number up rather than source down
11:53arohnercool
11:56arohnerdo you have any hints for things I should consider? I'm trying to get attuned to how you like to do things
13:18Chouserrhickey: clojure-contrib/clojurescript/clojurescript-compiler.patch is now up to date.
13:18ChouserI don't have any reason to think it will require any more changes.
13:23rhickeyarohner: nothing specific - sometimes it is just easier for me to fix something than analyze a patch
13:24rhickeyI guess the source for Clojure might indicate something, once you get your head around it. Also, Clojure itself betrays my preferences
13:25arohnerok. I'll keep reading the source :-)
13:35abrooksIs there a way to differentiate between an actual fn and things such as keywords, maps, etc. which just implement iFn?
13:36rhickeyabrooks: for what purpose?
13:36abrooksI want to differentiate between keyword arguments and fn created arguments.
13:36abrooksI could use class.
13:36Chouseroh, I though a final method in Java was like a C++ function that returns a const ref. It's not.
13:36abrooksWhich would tell me that the keyword is a keyword, I suppose.
13:37rhickeyThere's a keyword? predicate
13:37abrooksAnd 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:37abrooksi.e. where I actually want to know if something was created with fn or kin.
13:38arohnerkin?
13:38abrooksThings relating to fn (defn, #() etc.)
13:38arohneroh, right
13:38rhickeyright now, all fns created that way derive from AFn, but that's an implementation detail
13:39abrooksrhickey: Hm. Okay.
13:39rhickeyso (instance? AFn x) will work for you
13:40abrooksAFn isn't defined.
13:40rhickeyabrooks: I still don't see your use case though
13:40rhickeyclojure.lang.AFn
13:40Chouserabrooks: (instance? clojure.lang.AFn {}) is also true
13:40Chouserabrooks: just so you know.
13:40abrooksHrm.
13:40rhickeyoh, yeah, scratch that
13:40rhickeyno way then
13:41rhickeyyou'll need to give me a good use case
13:41Chouserabrooks: 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:42abrooksrhickey: 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:42rhickeyClojure's abstractions are oriented towards providing overlapping views of the world, not partitioning it
13:44abrooksrhickey: 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:44rhickeyabrooks: yes, I'm not arguing against it, just explaining why they aren't so much help here
13:45abrooksThe 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:45abrooksrhickey: I'm not complaining, BTW. :)
13:46rhickeyCan anyone reproduce Cliff's problem: http://groups.google.com/group/clojure/msg/bdc33bec4a01370f
13:47rhickeyI'm stuck on JDK 1.5 OS X here...
13:49abrooksrhickey: It works for me with Sun JDK 1.6.0.05.
13:49abrooks(Linux)
13:49rhickeyabrooks: you'll have to do a negative test, i.e. IFn and not keyword? and not collection? -> probably real function, maybe
13:50abrooksrhickey: Ah. That could work. Thanks.
13:50rhickeyon my side, I'd have to add a marker interface... not sure I want to do that
13:50abrooksUnderstood.
13:56Chouserboot.js (generated from boot.clj) is currently about 143KB
13:57rhickeyis that good or bad?
13:57Chouserthat's without the optional debugging output.
13:58Chouserwell, it's not fantastic.
13:59rhickeyI imagine with identifier compression that could become much smaller?
13:59ChouserjQuery is a popular lib that augments JS and browser DOM stuff -- 94KB
14:00Chouseryeah, I'm sure you're right. Can also gzip (which can be delivered to the browser directly).
14:00ChouserBut that's also without any of the Collections.
14:01ChouserSo it's probably a mostly meaningless number at this this point.
14:01ChouserSo I should have brought it up.
14:01ChouserSorry.
14:01Chouser:-)
14:02Chouserhm, there are also still things in boot.js that are completely useless.
14:07abrooksIs this the best I can do for (collection? ): (ancestors (class {}) java.util.Collection)
14:07arohnerrhickey: do you need the jdk or jre to run an existing clojure.jar?
14:08rhickeyabrooks: sorry, it's coll?
14:08abrooksOh, shoot. I missed that.
14:08rhickeyarohner: yes
14:08abrooksI've used that before.
14:08abrooksSheesh.
14:09abrooksChouser: We should group all of the predicates together in the docs. :)
14:09Chouserreturns boolean
14:09rhickeyfind-doc takes a regex string
14:10arohnerrhickey: but you can run with only a jre?
14:10Chouserarohner: right, no javac required
14:10rhickeyarohner: should work, yes
14:10arohnerI was thinking about cliff's problem. I have a windows box that I only use for outlook
14:12rhickeysomeone reproduced on XP SP3
14:18ChouserI've got XP SP2 in a vm here.
14:22rhickeyworks here in a vm on Server 2003, SP2
15:00rhickeyChouser: ClojureScript compiler patch applied (rev 1035)
15:04Chouserrhickey: cool, thanks.
15:05rhickeythank you for ClojureScript!
15:06Chouserheh, yeah, we'll see...
15:09ChouserDoes apply only work on clojure-defined functions?
15:09Chousernot Java methods?
15:09rhickeyright
15:09StartsWithKChouser: what parts of ClojureScript work now?
15:11ChouserStartsWithK: JS is emitted for any valid Clojure code (I think), so I'm working on the runtime support now.
15:11ChouserVars, lazy-cons, various bits and pieces of boot.clj work.
15:12StartsWithKyou will rewrite all clojure data structures in js yourself?
15:12Chouseryes, although I'm actually hoping to write them in Clojure. :-) We'll see.
15:13StartsWithKso, there is a chance clojure will become selfhosting?
15:14ChouserI've written PersistentVector, ArraySeq, LazyCons, and bits of some other stuff in JS.
15:14StartsWithKcan it access browsers dom?
15:14StartsWithKor any other js inside the browser?
15:14rhickeyStartsWithK: not yet, ClojureScript uses the Clojure compiler's analysis phase
15:15Chouseryes, access to browser JS and Dom feels like Java access.
15:16StartsWithKrhickey: any plans in making it selfhosted one day?
15:16StartsWithKclojure i mean :)
15:16ChouserThis has been working all week: http://n01se.net/paste/23H
15:17Chouserthat demonstrates DOM access, lazy seqs, macro expansion, and at least the skeleton of destructuring.
15:17rhickeyStartsWithK: 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:18StartsWithKChouser: have a link to generated js maybe? just to see how large output is
15:18drewrChouser: I didn't know you where working on this. Excellent!
15:19Chouserhttp://n01se.net/paste/Qhn
15:20Chouserthat's with all the debugging stuff turned on
15:21ChouserWithout that it's slightly more compact: http://n01se.net/paste/DlE1
15:24StartsWithKwow
15:24Chousersorry, did I scare you? :-)
15:24StartsWithKno, it looks great
15:25StartsWithKthere will be no need to gzip that, i was thinking it will produce much larger code
15:26StartsWithKi think you'll have your first customer next week :)
15:26Chouserwell, 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:27Chouserhm, now *you're* scaring *me*.
15:28StartsWithKi have very simple page, and i only serve json to very small js script, this looks like it can replace it.
15:30ChouserI 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:56Chouserlazy varargs are tricky
15:57rhickeyhow so?
15:58ChouserJS supports varags by making with an "arguments" object that's like an array of all args passed in
15:58Chousers/by making//
16:00Chousera cljs function that takes [a b & r] is emitted so that r takes the tail part of "arguments"
16:01Chousercljs 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:02ChouserI'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:02Chouserwhere c may be some kind of collection but is meant to be the 3rd actual argument to func.
16:04ChouserI 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:05Chouseroh! I have an extra "this" arg that I'll never use in a clojure-defined function. Hm...
16:05rhickeyyeah, that's a problem. Clojure uses AFn, RestFn and applyTo to handle this stuff
16:05ChouserI've been looking at AFn and applyTo... need to check RestFn
16:06Chouserhm. I think "this" might just do it...
16:13ozzileeChouser: 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:13Chouserozzilee: yessir! wait, arguments.callee, right?
16:14ozzileeChouser: Yeah, arguments.callee, you're right.
16:14Chouseryep, using that already. ...and indeed will probably be using it more for this problem.
16:15ozzileeAlrighty. FIgured you knew, just making sure.
16:15Chouseryep, thanks.
19:29cemerickit'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:30Chouserdo you really mean cyclic, or just interleving?
19:34cemerickChouser: hrm, probably interleaving
19:35cemerickChouser: congrats on the first cut of clojurescript, BTW. Looks like quite the genie you're releasing :-)
19:40cemericklooks 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