2008-07-25
| 03:12 | meredydd | Chouser: A-ha. |
| 03:12 | meredydd | Chouser: I had no idea what you were talking about, until I grepped the API document for ":state" |
| 03:13 | meredydd | and realised that what I thought was part of the paragraph about factory methods was in fact a description of another option. |
| 08:32 | meredydd | Hey...does the Java interop work with Java var-arg methods? |
| 08:33 | rhickey | there are no real var-arg methods in Java, they take arrays and, for now, to call them you have to pass arrays |
| 08:34 | meredydd | I'm aware that they're arrays behind the scenes |
| 08:34 | meredydd | I take it there's no tag you can examine to see who is and who isn't a var-arg method? |
| 08:36 | rhickey | there is, the problem is that Java uses types to detect the difference between passing an array (to match the array arg) and passing a non-array (which Java will turn into an array auto-magically) |
| 08:38 | rhickey | so it's (String/format "%d%d%d" (to-array [1 2 3])) for now |
| 08:55 | Chouser | probably warrants a mention on the java-interop page |
| 09:02 | cemerick | an impl of defbean (now called genbean) that I'm mostly happy with is finally done, along with a generalized approach for integrating it (and other gen-and-save-class usage) into a build process; hopefully, I'll get to writing it up for the group later today |
| 09:03 | rhickey | cemerick: cool, is there something you still wish you had for that? |
| 09:04 | cemerick | rhickey: not much, aside from the ability to define method impls in a gen-class body |
| 09:05 | cemerick | I ended up ditching the proxy approach entirely because invoking the polymorphic "makeNew" function was way too cumbersome |
| 09:07 | cemerick | a small thing would be to have a *verbose-gen-class* (or whatever) var that would println where gen-and-save-class is trying to save bytecode |
| 09:09 | lisppaste8 | rhickey pasted "group-by partition-by" at http://paste.lisp.org/display/64190 |
| 09:10 | cemerick | rhickey: oh, right the rumored, forthcoming name munging in gen-class would be *really* helpful in general |
| 09:19 | rhickey | Chouser: I think partition-by is a better name for what you were trying to do |
| 09:20 | rhickey | the version I pasted call f once per item |
| 09:30 | Chouser | yeah, group-by sound enough like relation-speak that it's better working on sets or maps. |
| 09:31 | Chouser | ok, very clever use of "identical?" on a lazy seq |
| 09:34 | Chouser | f gets called one extra time on the first item of each partition, doesn't it? for fv? |
| 09:35 | rhickey | could be drop-while ... (rest s) I guess, to avoid that |
| 09:36 | Chouser | ah, yep. |
| 09:37 | lisppaste8 | rhickey annotated #64190 with "partition-by with no redundant calls" at http://paste.lisp.org/display/64190#1 |
| 10:03 | lisppaste8 | blackdog pasted "no matching field?" at http://paste.lisp.org/display/64193 |
| 10:04 | blackdog | hi can someone tell me why i get a no matching field exception |
| 10:04 | blackdog | please |
| 10:05 | rhickey | blackdog: Woot doesn't derive from anything that has an actionPerformed method. You can't add methods by just putting them in the Clojure side |
| 10:05 | blackdog | oh, |
| 10:06 | blackdog | ok, i thought gen- classes were much more general than proxy, |
| 10:07 | rhickey | blackdog: they are, but you still have to make sure your methods are in the Java class itself, either by deriving or using the :methods argument to gen-class |
| 10:08 | rhickey | Java class signatures are not dynamic |
| 10:08 | blackdog | is there any way then to create a new class with some methods totally in clojure? |
| 10:09 | blackdog | i'm trying to attach some handlers to a gui designer, and it's looking for an object with actionPerformed methods |
| 10:10 | rhickey | the gen-class system partitions class definition into 2 parts - the part Java requires be static, i.e. the signature, and everything I can make dynamic (the implementation) |
| 10:10 | rhickey | the gui designer is looking for an instance of java.awt.event.ActionListener, no? |
| 10:11 | blackdog | well. it |
| 10:11 | blackdog | it's a runtime lib (jformdesigner) that reads xml descript |
| 10:11 | blackdog | and then you simply say setTarget(handlerobject) |
| 10:11 | blackdog | so i'm trying to setup the handler object |
| 10:11 | blackdog | in clojure |
| 10:12 | blackdog | the handler object is a pojo |
| 10:12 | blackdog | with just the correct methods names |
| 10:13 | blackdog | so i don't have anythign to derive from, but maybe i should investigate further the runtime api |
| 10:13 | blackdog | it may have more, |
| 10:13 | rhickey | well, you can proxy ActionListener, gen-class ActionListener, or gen-class :methods ... actionPerformed ... |
| 10:14 | blackdog | ok, i'll go off and think about that, thanks rhickey |
| 10:24 | blackdog | rhickey, gen-class :methods allows one to define the static part then? |
| 10:25 | rhickey | blackdog: everything in the gen-class defines the static part, but the :methods are just signatures. The definition still goes in the Clojure code as you had it |
| 10:26 | blackdog | but does the :methods get around having to derive or implement something - sorry for my denseness |
| 10:27 | rhickey | right, :methods are in addition to anything you get by deriving |
| 10:27 | blackdog | ok good, so that should work for me then |
| 10:28 | blackdog | ha! lift-off |
| 10:29 | blackdog | thanks! |
| 10:29 | rhickey | sure |
| 14:58 | cemerick | ach! why doesn't Google Groups support at least 80-column text? :-/ |
| 14:59 | cemerick | ach! why doesn't Google Groups support at least 80-column text? :-/ |
| 15:00 | rhickey | cemerick: yeah, if you have something substantial, you can attach it |
| 15:01 | cemerick | rhickey: where? I don't see any way to attach a file to a post. Or, do you mean upload it to the files section of the group? |
| 15:01 | Chouser | you may need to post via email, and attach to your email |
| 15:01 | rhickey | what he said |
| 15:02 | cemerick | ah. I should have done that. |
| 15:03 | rhickey | cemerick: you can still |
| 15:04 | cemerick | Yeah, I suppose I'll repost. It'll create a new thread, though, yes? |
| 15:04 | rhickey | or you can follow up to your original message with a short note and the attached file |
| 15:07 | cemerick | ah, I'll delete the original and send an email |
| 15:13 | cemerick | much better |
| 15:22 | rhickey | cemerick: a couple of thoughts, using the current ns as a name is very troubling - I know that's on me to provide defs of qualified names |
| 15:22 | rhickey | as far as get/set, setProperty is a problem - while it would be familiar looking, it won't be familiar behaving, as it doesn't modify the object in place. |
| 15:26 | rhickey | anyone want to take a crack at gen-interface? should be really easy, learn a little bytecode gen? |
| 15:30 | cemerick | rhickey: using the current namespace for the method impls and classname certainly isn't "hygenic", but that's what the typical usage would be anyway |
| 15:31 | cemerick | I don't mind at all having a setProperty that returns a new Foo, rather than having mutable semantics. That might freak out some Java/.NET people, but that's better than having broken equals semantics. |
| 15:31 | Chouser | gen-interface would take a single class, no options, and do the minimal gen-class equivalent for it? |
| 15:31 | rhickey | Chouser: no, allow the definition of an interface from scratch, just signatures |
| 15:31 | Chouser | I was thinking I might poke at gen-class function name mangling if nobody beat me to it. |
| 15:32 | Chouser | ah. |
| 15:34 | rhickey | Chouser: if '.' prefix is too clever, what about '-' ? |
| 15:35 | rhickey | other genclassers please chime in |
| 15:35 | Chouser | I was thinking I'd do something lengthy (and internally configurable), and just provide a def-gen-method to match the same mangling. |
| 15:36 | cemerick | we're talking about mangling names so as to avoid the name clashes with referred namespaces? |
| 15:36 | rhickey | cemerick: yup |
| 15:36 | Chouser | at least that would could be done now safely, and allow any code written using it to be forward compatible with any more minimal mangle that might be chosen later. |
| 15:36 | Chouser | s/would// |
| 15:37 | cemerick | I'd definitely avoid '.' as a prefix. A '-' prefix would be a 99% solution, certainly. |
| 15:37 | rhickey | the problem with a mangle hidden by a special def is - how do you call/test it? It's still a normal function |
| 15:37 | Chouser | i'd be worried (defn -foo) looks a lot like (defn- foo) |
| 15:39 | Chouser | you might need to have "this" bound when you call it manually, right? there aren't any other special rules? |
| 15:39 | rhickey | maybe, but both are implementation details, editor coloring clears up pretty well |
| 15:39 | cemerick | I've always liked the python convention of a double-underscore *shrug* |
| 15:39 | rhickey | Chouser: yes this, no, they are regular fns |
| 15:39 | rhickey | cemerick: Lisp uses - instead --method would be ok too |
| 15:39 | Chouser | but you could test by creating an instace of the class and calling them as methods -- that would get "this" right, and follow any mangling. |
| 15:40 | Chouser | python uses double-underscore surround the name, which makes it hard to say aloud: __foo__ |
| 15:40 | cemerick | rhickey: yeah, I was just replying to Chouser's concern about a prefixed '-' appearing too similar to defn- |
| 15:40 | Chouser | I guess "dash dash foo dash dash" is a bit better. |
| 15:40 | rhickey | as methods they are less powerful than fns, not first-class |
| 15:40 | rhickey | can't map etc |
| 15:41 | rhickey | Chouser: we have to stay off the end, already used for types |
| 15:41 | Chouser | ah, right. ok, so dump def-gen-method, and we'll just finish this mangling detail discussion right now. :-) |
| 15:41 | cemerick | Chouser: actually, "private" names in python usually have double-underscore only in front of the name -- that's what triggers name mangling |
| 15:41 | Chouser | cemerick: oh! ok. |
| 15:42 | Chouser | I'm fine with a -- prefix. *shrug* |
| 15:42 | Chouser | or a .. prefix, or #! or whatever. ;-) |
| 15:43 | cemerick | yeah, I really don't think it matters, outside of not using '.'. :-) |
| 15:45 | cemerick | actually, '-' sorts ahead of most other chars, which would be unfortunate for tools, etc. |
| 15:46 | rhickey | another potential issue with -- is it get munged to __, which is a name component I presume the Clojure implementation "owns" |
| 15:46 | Chouser | we don't really want anything starting with # since that triggers reader magic, right? |
| 15:46 | cemerick | pipes would work, and they sort after all other ascii chars; not so pretty, tho |
| 15:46 | rhickey | Chouser: right! |
| 15:46 | Chouser | * mean "global" |
| 15:46 | Chouser | % is already taken |
| 15:47 | rhickey | I'm reserving all other special chars |
| 15:47 | Chouser | @ and ~ are already taken pretty globally as well, right? |
| 15:47 | Chouser | oh. so it has to be -, ., or alphanumeric? |
| 15:47 | rhickey | this isn't worth a special character |
| 15:48 | Chouser | and since I think a prefix makes a lot of sense (compared to a postfix), it's really just alpha. |
| 15:48 | Chouser | -- is apparently dangerous, so either just "-" or something like "method-" or "gen-" |
| 15:49 | cemerick | well, these names aren't going to be accessed with any kind of regularity, right? |
| 15:49 | Chouser | the only other requirement being that it is very unlikely for a Java class to use in a normal method name. |
| 15:49 | cemerick | (by non-generated code, I mean) |
| 15:49 | rhickey | I'm not sure sorting first is bad, when looking at a ns dedicated to a class implementation, the methods will sort first |
| 15:53 | Chouser | any kind of prefix will have them sorted together. Having them first seems a little nicer than having a bunch of "method-*" between your "loo" and your "noo" functions. |
| 15:53 | rhickey | cemerick: right, the names might only rarely be used |
| 15:54 | Chouser | "-m-" prefix |
| 15:54 | cemerick | Chouser: +1 |
| 15:54 | Chouser | that would sort first, stand out more than just "-", suggests a method without being verbose |
| 15:55 | Chouser | and if that gets mangled to "_m_" I think we've avoided better than 99.9% of Java method names. |
| 15:56 | rhickey | Chouser: the only conflict with - is if a class defined public foo and _foo, even then, one becomes _foo and the other __foo |
| 15:57 | rhickey | alpha prefixes dramatically reduce readability imo, - disappears, but m doesn't |
| 15:58 | Chouser | I agree - is technically sufficient, I'm just not convinced the difference between (defn- foo) and (defn -foo) is unmistakable. |
| 15:58 | Chouser | hm. |
| 15:59 | rhickey | in my editor defn- foo is all black and defn -foo is purple/blue |
| 16:02 | rhickey | ericthor: any thoughts if you are there? (enclojure might have more genclass than anyone) |
| 16:07 | rhickey | or we could use _, to remind us we're in Java-land |
| 16:07 | rhickey | (defn- foo (den _foo |
| 16:08 | rhickey | (defn- foo (defn _foo |
| 16:17 | Chouser | works for me, but either is fine. |
| 16:18 | rhickey | on an unrelated note: (derive java.util.Collection `aggregate) will work |
| 16:19 | rhickey | i.e. superimpose your own taxonomy on existing classes |
| 16:40 | mebaran151__ | is there a good way to get an arbitrary hash for an arbitrary object that would ensure uniqueness on value: I'd like to use this string representation for a primary key functionality in an app i'm writing |
| 16:42 | Chouser | sounds like you want sha1 or md5 |
| 16:51 | mebaran151__ | for like a whole map though? |
| 16:52 | mebaran151__ | like I'd like to be able to just take a random hashmap I created in clojure and run a function on it to return a string |
| 16:52 | mebaran151__ | that would be unique over the values of that hash |
| 16:52 | mebaran151__ | internally, that must be how clojure handles equality I would think |
| 16:55 | Chouser | whole maps have a hash value, but there's no guarantee they're unique -- when placed in a map or set a value comparison needs to be done to make sure you've really got the right item |
| 16:55 | Chouser | I think. That's normal for hashes, though. |
| 16:56 | Chouser | For equality you can use "identical?", but that'll give you false negatives. To be sure, I think the whole collection is compared. |
| 16:58 | mebaran151 | yeah |
| 16:58 | Chouser | so if you want a guaranteed-unique hash, I think you'd have to use md5 or sha1 on the whole value. |
| 16:59 | mebaran151 | it's a cool little project: trying to layer a fs db written in clojure |
| 16:59 | mebaran151 | I might just go with my old idea of incrementing a string |
| 16:59 | Chouser | If the value is made up of just well-printable clojure objects, you could print to a string and run sha1 on it. |
| 17:00 | mebaran151 | oh that's actually decent idea |
| 17:00 | mebaran151 | yes |
| 17:00 | mebaran151 | most of the objects will be well printable |
| 17:00 | mebaran151 | I'm actually planning on internally bucketing the objects via their printed forms anyway |
| 17:19 | Chouser | woo! I've been stabbing in the dark, and finally hit my target. Now I just have to understand it... |
| 17:20 | rhickey | Chouser: what target? |
| 17:20 | Chouser | prepending a - on function names but not method names. ;-) |
| 17:20 | Chouser | It's taken me, what, an hour? It would have taken you 15 seconds. |
| 17:21 | Chouser | I'm trying to find where we discussed before which names you wanted mangled and which you wanted to leave alone. |
| 17:21 | rhickey | Chouser: ah, yeah, it's a bit thick in there |
| 17:21 | Chouser | heh. yeah. But I don't know a better way to learn it. |
| 17:23 | Chouser | ah, here it is. "the names manually given to genclass would remain unmunged? :factory, :state, etc?" |
| 17:25 | rhickey | I don't know, seems like the same arguments might apply - these names are dictated by outside requirements and could just as easily bang into clojure names - prefixed with _ they never would |
| 17:25 | rhickey | doing them all is probably way easier |
| 17:26 | Chouser | yeah. doing them all is (maybe?) done. |
| 17:26 | rhickey | cool! |
| 17:26 | Chouser | but not all the names are dictated -- init, state, etc. |
| 17:26 | rhickey | state is a field |
| 17:27 | Chouser | oh, "public". ok. |
| 17:27 | rhickey | I think it will be easiest for users too, to know the rule is clojure implementations of Java gen methods begin with _ |
| 17:28 | rhickey | ? |
| 17:28 | rhickey | public? |
| 17:28 | Chouser | I knew :state was a field, but I didn't realize it was public. |
| 17:28 | rhickey | yes, public and final, so no getter/setter |
| 17:30 | Chouser | well, maybe I'm completely missing something (seems likely), but my change is to replace genclass line 276: (. gen push (str "_" v)) |
| 17:30 | Chouser | seems to work as expected. |
| 17:31 | Chouser | perhaps I need to test ctors, factories, etc. |
| 17:32 | rhickey | what about the lookup side? |
| 17:33 | rhickey | line 187? |
| 17:35 | blackdog | i'm trying to define a function defn as the body part of a macro |
| 17:36 | blackdog | and then using that as part of gen-and-load-class |
| 17:36 | blackdog | definition, but the function name has a _var appended |
| 17:36 | blackdog | [['wootActionPerformed [ActionEvent] Object]] |
| 17:37 | blackdog | Caused by: java.lang.ClassFormatError: Illegal field name "user/wootActionPerformed__var" in class igw/forms/Woot |
| 17:37 | blackdog | is that just a dumb thjing for me to try? |
| 17:38 | rhickey | blackdog: want to paste your macroexpansion? |
| 17:38 | blackdog | oh, i'll try, haven't really used that |
| 17:38 | blackdog | do i just stick macroexpand around the macro call? |
| 17:39 | rhickey | if you are having a macro problem, macroexpand is the first thing you should try, stick macroexpand around the *quoted* call |
| 17:39 | rhickey | (macroexpand '(my-macro x y z)) |
| 17:39 | blackdog | ok |
| 17:40 | rhickey | if your macro expands into another macro, try macroexpand-1 first |
| 17:42 | blackdog | is there a pretty print for a macro? |
| 17:42 | blackdog | macroexpand? |
| 17:42 | rhickey | no pprint yet |
| 17:42 | blackdog | k |
| 17:42 | rhickey | emacs? |
| 17:43 | blackdog | no, i'm using jedit :( |
| 17:44 | lisppaste8 | blackdog pasted "macroexpand" at http://paste.lisp.org/display/64223 |
| 17:45 | blackdog | i don't see _var in the macroexpand |
| 17:46 | lisppaste8 | blackdog pasted "stacktrace" at http://paste.lisp.org/display/64225 |
| 17:47 | blackdog | the methods section in the macro itself is hard coded, would be replaced |
| 17:48 | rhickey | user/wootActionPerformed is not a valid method name, you have to generate an unqualified name |
| 17:48 | rhickey | the __var is added by the implementation of gen-class, but is not the problem |
| 17:50 | blackdog | in the macro it tries to do (in-ns ~kls) to set the ns before doing the defn of wootActionPerformed |
| 17:50 | rhickey | that's not going to work |
| 17:50 | blackdog | ok, |
| 17:52 | blackdog | because i;'m missing something fundamental or just that it's a daft thing to do? |
| 17:54 | rhickey | actually, I'm not sure what you are trying to do, but one trick is to fix your expansion by hand and see if it works before fixing the macro. Always start a macro with a by-hand implementation of the expansion that works, then tweak the macro until it expands to the same thing |
| 17:55 | blackdog | ok thanks for that, |
| 17:55 | rhickey | trying to get a macro and the expanded logic right at the same time is a recipe for pain |
| 17:55 | blackdog | ok |
| 18:20 | Chouser | rhickey: don't feel you need to walk me through this -- it'd probably be easier for you to just do it yourself. But if you want to answer my questions instead... |
| 18:21 | Chouser | If I mangle line 187 my tests fail. |
| 18:22 | Chouser | is it doing a lookup in the class there? I don't want to mangle that, of course. |
| 18:48 | rhickey | Chouser: I see - you are not changing the names of the static fields, just the var symbols - then you don't need line 187 changed |
| 19:04 | arohner | why can't I peek at a range? |
| 19:04 | arohner | user=> (peek (range 1 10)) |
| 19:04 | arohner | (Peek (range 1 10)) |
| 19:04 | arohner | java.lang.ClassCastException: clojure.lang.Range |
| 19:04 | arohner | java.lang.ClassCastException: clojure.lang.Range |
| 19:04 | arohner | at clojure.lang.RT.peek(RT.java:454) |
| 19:04 | arohner | at clojure.fns.clojure.peek__398.invoke(boot.clj:757) |
| 19:04 | arohner | at clojure.lang.AFn.applyToHelper(AFn.java:184) |
| 19:06 | arohner | user=> (peek '(1 2 3 4 5 6 7 8 9 10)) |
| 19:06 | arohner | (peek '(1 2 3 4 5 6 7 8 9 10)) |
| 19:06 | arohner | 1 |
| 19:17 | Chouser | peek is for lists, vectors, and queues. For generic seqs, you can just use first. |
| 19:18 | Chouser | (first (range 1 10)) |
| 19:18 | arohner | ok |
| 19:18 | arohner | what is the difference between a seq and a list? |
| 19:19 | Chouser | oh, apparently peek is defined on IPersistentStack, which is implemented by list, vector and queue. |
| 19:19 | Chouser | arohner: that's a great question with a subtle answer. |
| 19:20 | arohner | yeah, I saw the IPersistentStack cast |
| 19:20 | arohner | that explained the stack trace, but didn't help me understand the issue :-) |
| 19:20 | Chouser | seq is an interface, specifically ISeq. Every ISeq provides "first" and "rest" |
| 19:21 | Chouser | every collection in clojure can viewed through a seq. For example: (seq {:a 1, :b 2}) |
| 19:22 | Chouser | A list (PersistentList) is a concrete data structure, a singly-linked list. I happens to provide the ISeq interface itself directly, unlike most other collections. |
| 19:23 | Chouser | user=> (def x (list 1 2 3)) |
| 19:23 | Chouser | user=> (identical? x (seq x)) |
| 19:23 | Chouser | true |
| 19:23 | Chouser | user=> (def y {:a 1, :b 2}) |
| 19:23 | Chouser | user=> (identical? y (seq y)) |
| 19:23 | Chouser | false |
| 19:24 | arohner | ok, so the simple answer is that peek and pop are defined on things that have a stack interface |
| 19:24 | Chouser | right |
| 19:28 | arohner | thanks for the help |
| 19:29 | Chouser | np |
| 19:51 | mebaran151_ | is it possible to join a lazy sequence to non-lazy sequence |
| 19:53 | mebaran151_ | I want to do something like join at the head of list (1 2 3) to an infinite sequence of 4's for instance |
| 19:54 | mebaran151_ | in the repl, lazy-cons produced the infinite sequence I didn't want it to do |
| 19:55 | Chouser | the repl will try to print all of a seq, lazy, infinite, or otherwise. |
| 19:56 | Chouser | (take 10 (lazy-cat (list 1 2 3 4) (repeatedly #(identity 4)))) |
| 19:57 | Chouser | I'm not sure what the best way is to create an infinite seq of 4 |
| 19:57 | Chouser | (take 10 (lazy-cat (list 1 2 3 4) (cycle [4]))) |
| 19:58 | Chouser | ah! |
| 19:58 | Chouser | (take 10 (lazy-cat (list 1 2 3 4) (repeat 4))) |
| 20:26 | arohner | is there a library call that gives me all of the permutations of two lists? |
| 20:27 | Chouser | aren't permutations usually figured from a single list? |
| 20:27 | arohner | I want to pass two lists (range 1 10) (range 1 10) and get a list of tuples (1 1) (1 2) ... (10 9) (10 10) |
| 20:27 | Chouser | ah. for |
| 20:27 | arohner | maybe I'm using the wrong term |
| 20:27 | slava | the cartesian product of two lists? |
| 20:27 | mebaran151_ | thanks Chousefr |
| 20:28 | Chouser | (for [x (range 10) y (range 10)] [x y]) |
| 20:28 | arohner | ah, thanks |
| 20:28 | mebaran151_ | hey Chouser, how can I tell if my recursion is occurring in the tail position |
| 20:28 | arohner | if you use (recur) it will be in the tail position, or you get an exception |
| 20:28 | Chouser | if the compiler lets you use (recur), then it's tail position. |
| 20:29 | mebaran151_ | I'm trying to call recur inside an if conditional but clojure continually complaines |
| 20:29 | Chouser | what he said. |
| 20:29 | slava | why is recur in non-tail position prihibted? |
| 20:30 | mebaran151_ | (def next-id (fn this |
| 20:30 | mebaran151_ | ([id] this (this id *max*)) |
| 20:30 | mebaran151_ | ([id max] (loop [i id m max] |
| 20:30 | mebaran151_ | (if (< (first i) (first m)) |
| 20:30 | mebaran151_ | (conj (incr (first i)) (rest id)) |
| 20:30 | mebaran151_ | (conj (base (first i)) |
| 20:30 | mebaran151_ | (recur (rest i) (rest m)))))))) |
| 20:30 | mebaran151_ | (sorry about the flood) |
| 20:30 | slava | mebaran151_: recr is not in tail position there because its result is passed to conj |
| 20:30 | mebaran151_ | ah |
| 20:30 | slava | but i don't understand the limitation |
| 20:30 | arohner | because recur guarantees the stack size will not grow |
| 20:30 | mebaran151_ | so how would I refactor this |
| 20:31 | slava | i'm not sure why you'd have recur at all |
| 20:31 | mebaran151_ | well I'm recursively calling next-id |
| 20:31 | slava | so just call it explicitly i guess |
| 20:31 | mebaran151_ | my id's are basically a seq of integers |
| 20:31 | mebaran151_ | that can be of arbitrary length |
| 20:31 | arohner | mebaran, you probably need a loop position inside your ([id max]) case |
| 20:32 | mebaran151_ | it's there |
| 20:32 | arohner | oh, right |
| 20:32 | mebaran151_ | maybe there's a better way about it using reduce in some fancy way |
| 20:33 | arohner | you can self-call without the recur as well, you just aren't guaranteed the stack size won't grow |
| 20:33 | mebaran151_ | I'm trying to basically do carry addition |
| 20:33 | slava | so is recur just an assertion basically that has no effect on the compiled code? |
| 20:33 | slava | how do you assert that a tail call to another function is a tail call? |
| 20:33 | arohner | no |
| 20:34 | arohner | the explanation I've heard is that the JVM doesn't have the opcodes to do a real tail call |
| 20:34 | arohner | recur only goes to designated places in your own function |
| 20:34 | arohner | a (loop), the top of the function, and a few other places |
| 20:35 | arohner | recur target is the term I'm looking for |
| 20:35 | slava | oh, right |
| 20:35 | slava | the JVM can't tail call, I forgot about that |
| 20:36 | slava | you can just do a goto within the current method |
| 20:38 | Chouser | slava: right, that's what recur does under the covers. |
| 20:38 | Chouser | which is why it has to be in tail position. |
| 20:39 | arohner | so I have a for loop that looks like |
| 20:39 | arohner | (for [x (range 1000) |
| 20:40 | Chouser | "on lisp" has a section which is where I learned how to move recursion to tail position. |
| 20:40 | arohner | (for [x (range 1000) |
| 20:40 | arohner | y (range 1000)] |
| 20:40 | arohner | (if foo (print "foo") nil)) |
| 20:40 | arohner | when I evaluate that, the screen is filled with prints to nil |
| 20:41 | arohner | is there a way to not print all the nils? |
| 20:41 | Chouser | mebaran151_: instead of returning your accumulating value, pass it forward to the next recursive call, and finally return the whole thing at the end. |
| 20:42 | mebaran151_ | I'm doing something similar with reduce right now |
| 20:42 | slava | that's something that the compiler should do though, not hte programmer |
| 20:42 | mebaran151_ | I agree with slava here |
| 20:42 | Chouser | arohner: you're trying to program for side-effects, which will be fighting uphill in clojure. What are you trying to return? |
| 20:43 | slava | if the result of the non-tail-recursive call is the input to an associative operation, then its pretty easy to convert it to use an accumulator |
| 20:43 | mebaran151_ | oh I'm trying to transform an array of ints as follows |
| 20:43 | slava | if its the input to cons, you convert it to an accumulator and reverse the list at the end |
| 20:43 | slava | pretty simple transformation to make |
| 20:43 | slava | the problem with doing it by hand is that its error-prone and you get a proliferation of auxilliary functions |
| 20:44 | mebaran151_ | no int can be greater than the max value, and I'm incrementing from the left |
| 20:44 | arohner | ah, figured it out |
| 20:44 | arohner | :while |
| 20:46 | arohner | ah, I should be using :when and :while |
| 20:46 | arohner | I'm not really programming for side effects, I was just being lazy because I only cared about the last value |
| 20:46 | arohner | doing project euler stuff, so I just needed to see the last value and type it in |
| 20:49 | Chouser | slava: I'd be interested to see a patch to Clojure that does that. |
| 20:52 | Chouser | arohner: yeah, I just meant with "print". project euler is a great way to learn a new language. |
| 21:03 | mebaran151 | my method didn't work: how would you transform my example? |
| 21:09 | Chouser | incr is the same as inc? |
| 21:09 | Chouser | oh, incr and base are your own? |
| 21:10 | mebaran151 | yeah |
| 21:10 | mebaran151 | it's a string incrementer of my own devising |
| 21:11 | mebaran151 | kind of like Ruby's |
| 21:16 | Chouser | what does next-id return? I'm guessing a list? |
| 21:17 | rhickey | Chouser: a patch to Clojure that does what? |
| 21:17 | Chouser | does tail-call optimization at compile time. |
| 21:17 | rhickey | on a self-call? |
| 21:18 | Chouser | no, I kinda like the tail-position assertion of "recur". For non-tail-position and mutual-recursion cases. |
| 21:19 | Chouser | I think that's what slava was talking about, anyway. |
| 21:19 | rhickey | the JVM can't do mutual recursion TCO |
| 21:20 | Chouser | Yeah, I know. My understanding of what slava was saying is that the compiler could overcome the JVM's runtime weakness there. |
| 21:20 | rhickey | It can't |
| 21:20 | rhickey | I think he was just talking about not needing recur on self-call |
| 21:21 | Chouser | self-call in a non-tail position? |
| 21:21 | rhickey | self-call in tail position |
| 21:21 | Chouser | yeah, that's not interesting. :-) |
| 21:22 | rhickey | the reason that I don't is because of the expectation it creates among Scheme programmers |
| 21:24 | rhickey | this way it's easy to explain - there's no TCO, only recur is guaranteed non-stack-consuming |
| 22:08 | slava | Chouser: i was talking about an optimization that converts non-tail-calls into a tail call with an acculuator |
| 22:08 | slava | eg, (defun (fac x) (if (< x 3) x (* (fac (- x 1)) x))) |
| 22:08 | slava | this is not tail recursive |
| 22:08 | slava | but its easy to turn it into a tail recursive function |
| 22:12 | slava | people will expect a lisp compile to do this, so it will be surprising to many if clojure doens't |
| 22:12 | slava | they'll write fnctions that blow the stack without realizing it |
| 22:21 | Chouser | slava: are you working on a patch? |
| 22:23 | Chouser | Is it common for CL or Scheme to optimize non-tail calls? I haven't gotten that impression from the little reading I've done. |
| 22:34 | Chouser | can you guarantee all self-calls would be converted to tail-call? If not, that could cause even more surprising behavior. |
| 22:37 | albino | I doubt he's working on a patch, he has his own language to contend with |
| 22:38 | Chouser | ah |
| 22:42 | slava | Chouser: you can't convert all calls into tail calls of course |
| 22:42 | slava | its surprising inasmuch as any compiler optimization is |
| 22:43 | slava | but that's not an argument for non-optimizing compilers :) |
| 22:44 | Chouser | well if the difference between optimizing and not is a bit of speed, that's one thing. If it's the difference between blowing the stack and crashing or not, that kinda matters... |
| 22:45 | slava | does java crash when you exceed a fixed-size call stack allocation or does it grow the stack? |
| 22:46 | slava | i haven't done any java since 1.4 or so, i guess many things have changed |
| 22:47 | Chouser | if I do infinite recursion, it crashes |
| 22:47 | Chouser | ((fn x [] (x))) |
| 22:47 | Chouser | boom |
| 22:47 | slava | because it fills up the heap? |
| 22:48 | albino | slava: how could one tell the difference? |
| 22:49 | slava | if the stack has a fixed size, then converting list operations to tail recursion with a list accumulator will be a win |
| 22:49 | slava | otherwise, not so much |
| 22:50 | slava | in both cases converting arithmetic to tail recursive form is a win because you'll run in O(1) space and not O(n) |