2008-07-26
| 01:55 | jykrd | hi |
| 01:56 | jykrd | I'm trying to figure out how to convert java into clojure. As an exercise, I'm trying to convert this: http://www.devdaily.com/java/edu/pj/pj010016/pj010016.shtml |
| 01:56 | jykrd | (runs system commands) |
| 01:57 | jykrd | And to go off of, I'm trying to glean the method from the gui example on the wiki |
| 01:58 | jykrd | so the first thing I did was converted this: Process p = Runtime.getRuntime().exec("ps -ef"); |
| 01:58 | jykrd | to this: #^Process p (. (. (Runtime getRuntime) exec (str "ps -ef"))) |
| 01:59 | jykrd | (inside a let) |
| 01:59 | jykrd | But I get the feeling that's not the correct way |
| 02:00 | jykrd | I'm not worried about the str so much.. more so the #^Process at the beginning |
| 02:05 | mebaran151 | I think you can drop that |
| 02:05 | mebaran151 | clojure does a little bit of type inference |
| 02:06 | jykrd | so does Runtime.getRuntime() always return a Process? I was figuring that line was doing type casting, or casting to the super, or something |
| 02:06 | jykrd | Would have expected to see Runtime p = Runtime.getRuntime() |
| 02:07 | mebaran151 | I'm not super familiar with that call |
| 02:07 | jykrd | ok |
| 02:07 | mebaran151 | but yeah, I suppose if the javadocs say so it must be true |
| 02:07 | jykrd | well, from the repl, that compiles, so I guess I'll keep trucking |
| 02:08 | mebaran151 | I think it just takes those comments as hints though |
| 02:08 | mebaran151 | so even if you were wrong, it would valiantly try |
| 02:08 | jykrd | now, to do this in a let : BufferedReader stdInput = new BufferedReader(new |
| 02:08 | jykrd | InputStreamReader(p.getInputStream())); |
| 02:08 | mebaran151 | I hate how long java names are |
| 02:09 | jykrd | would is there a more graceful way than: stdinput (new BufferedReader (new InputStreamReader (. p getInputStream))) |
| 02:10 | mebaran151 | not in java land |
| 02:10 | jykrd | ok.. Well that might work then |
| 02:10 | mebaran151 | but I don't think it would be much more graceful in java anway |
| 02:10 | jykrd | well, I mean, in clojure, is there a better way than calling a new withing a new |
| 02:13 | mebaran151 | you can use doto for a lot of this |
| 02:13 | mebaran151 | oh yeah |
| 02:13 | mebaran151 | ClassName.. make work |
| 02:13 | mebaran151 | I think I saw that in the docs |
| 02:13 | mebaran151 | it's a macro that automagically instantiates that class |
| 02:14 | jykrd | huh |
| 02:14 | jykrd | it works |
| 02:18 | jykrd | You know what they need in the docs, under the "clojure for java people" section.. is a "converting a 'while' structure into clojure" and the like |
| 02:22 | mebaran151 | yeah I'm using it for a cool webstartup that's partially written in scala |
| 02:22 | mebaran151 | it would be really nice if the docs weren't so non-existent |
| 02:23 | mebaran151 | or at least if they could wiki the doc section so we could add comments |
| 02:23 | mebaran151 | you wouldn't use a while loop in clojure thouhg: it's not very functional |
| 02:23 | mebaran151 | what are you trying to do |
| 02:23 | mebaran151 | usually map or reduce will get you 90 percent there.... |
| 02:24 | jykrd | trying to do: |
| 02:24 | jykrd | while ((s = stdInput.readLine()) != null) { |
| 02:24 | jykrd | System.out.println(s); |
| 02:24 | jykrd | } |
| 02:25 | mebaran151 | reading a file eh |
| 02:26 | mebaran151 | I've never really understood how do that functionally but we could write a monster hybrid for kicks |
| 02:26 | jykrd | And I'm actually thinking: (loop [s (. stdInput readLine)] |
| 02:26 | jykrd | (when-not (= s nill) |
| 02:26 | jykrd | (.. System out (println (str s))))) |
| 02:26 | jykrd | std input |
| 02:26 | mebaran151 | I think loop is only for recur |
| 02:27 | jykrd | oh yea, and I'd have to add a recur at the end I guess |
| 02:27 | mebaran151 | you could do something using |
| 02:27 | jykrd | with the same condition as when-not or something |
| 02:27 | mebaran151 | (repeatedly (. stdInput (readline)) |
| 02:27 | jykrd | hmm |
| 02:27 | mebaran151 | that builds a lazy list of readlines |
| 02:27 | jykrd | really? |
| 02:27 | mebaran151 | yeah |
| 02:27 | mebaran151 | cloisng the file might be problematic though |
| 02:28 | mebaran151 | if you close the file and try to run that |
| 02:28 | jykrd | its std input, not a file |
| 02:28 | mebaran151 | some sort of exception has to be thrown |
| 02:28 | mebaran151 | stdinput my bad |
| 02:28 | jykrd | you remember which doc page the repeatedly was on? |
| 02:28 | mebaran151 | the api is all doc'ed alpabetically |
| 02:28 | mebaran151 | do a search text in firefox for lazy |
| 02:29 | mebaran151 | and you'll find a lot of cool macros rick wrote |
| 02:30 | jykrd | gah, it's all so terse |
| 02:33 | jykrd | so, (repeatedly (.. System out (println (. stdInput (readline)))) |
| 02:33 | jykrd | or something? |
| 02:39 | jykrd | gah.. looping control structures.. I'm having such a hard time wrapping my head around them in a functional way |
| 02:40 | mebaran151 | yeah |
| 02:40 | mebaran151 | io is not the best place to learn about fp |
| 02:42 | mebaran151 | it would more like be something like |
| 02:44 | mebaran151 | repeatedly (fn [] (.. System (println (.stdInput (readline)) |
| 02:50 | jykrd | eeeoooh |
| 02:56 | mebaran151 | you might put some more conditional logic in there |
| 02:56 | mebaran151 | to represent each iteration |
| 02:57 | jykrd | yea, it's flooding the screen with nills |
| 02:57 | mebaran151 | oh yeah |
| 02:57 | mebaran151 | don't directly type in repeatedly |
| 02:57 | mebaran151 | it will try to evaluate it fully |
| 02:57 | mebaran151 | you have set it equal to something |
| 02:57 | mebaran151 | like (def lines (repeatedly what .....)) |
| 02:57 | mebaran151 | then do first and rest stuff on it |
| 02:57 | jykrd | huh |
| 02:58 | jykrd | hmm |
| 02:58 | mebaran151 | to print something it has to evaulate it |
| 02:58 | mebaran151 | prn recursive tries to print everything, which fails for an infinie list |
| 03:00 | mebaran151 | just keep a little pointer around and iterate until nil |
| 03:01 | jykrd | oh my gosh, it almost works :) |
| 03:01 | hoeck | or use (take n (repeatedly ..)) |
| 03:04 | jykrd | wow.. I've got output |
| 03:05 | jykrd | I'm running the my linux' ps command from clojure ! :) |
| 03:06 | jykrd | now I just need to round this out into a tool of some sort and I might be able to start doing shell scripting with clojure |
| 03:06 | jykrd | you guys kick ass.. thanks.. I need to sleep now, but I'm glad I stayed up :) night |
| 03:07 | mebaran151 | np |
| 03:07 | mebaran151 | just looking at line-seq |
| 03:08 | mebaran151 | I think that clojure should have some more clojurey file openers |
| 03:09 | hoeck | mebaran151: what about "slurp"? |
| 03:10 | mebaran151 | isn't that only for valid clojure files though |
| 03:10 | mebaran151 | you know |
| 03:10 | mebaran151 | better reading and writer |
| 03:11 | mebaran151 | and maybe some more in depth control structures |
| 03:11 | mebaran151 | *deserializing structures |
| 03:11 | mebaran151 | boy am I tired |
| 03:12 | mebaran151 | also I was workng with a colleague in clojure (which we've snuck in our project via the all knowing JVM) |
| 03:12 | mebaran151 | we think that it would be relaly nice if you guys could open the clojure api to wiki comments |
| 03:12 | mebaran151 | sort of like php |
| 03:13 | hoeck | aha, i don't know much about php |
| 03:14 | mebaran151 | I don't either |
| 03:14 | mebaran151 | but when I had to fix one script |
| 03:14 | mebaran151 | it was really nice that the php docs had a comment section |
| 03:14 | mebaran151 | and other users had recorded their thoughts |
| 03:14 | mebaran151 | spruced with a little ajax, people could note whether or not to look at the comments |
| 03:14 | mebaran151 | sometimes the function is sort of unclear how it works, esp with things like refs and agents |
| 03:16 | mebaran151 | the massive alphabetized list of functions is also rather unappealing: maybe some sort of tagging system to relate what their good for that people could use to sort them |
| 03:18 | mebaran151 | also it's almost idiotic that we don't have a siimple way just to eval code and I have go through hell just to turn a string into a BufferedReader for eval like semantics |
| 03:18 | hoeck | yeah, the api docs are really huge and hard to search, especially if you don't know which function to search for |
| 03:20 | mebaran151 | exactly |
| 03:20 | mebaran151 | I'm currently writing a little object store in clojure sort of rucksack |
| 03:20 | hoeck | if you're on the repl, you can use (find-doc "file") to search for all functions containing "file" in their docstring |
| 03:21 | mebaran151 | you have any suggestions, especially with regards to efficiently serializing a b* tree |
| 03:22 | hoeck | no, not really |
| 03:22 | mebaran151 | it's hard to find good byte reading stuff on clojure |
| 03:23 | mebaran151 | and I don't know much about training the interpreter to be more efficient at slurping in objects |
| 03:24 | hoeck | what kind of objects? |
| 03:25 | mebaran151 | arbitrary hashes really |
| 03:26 | mebaran151 | I do all the db management stuff using metadata which is really rather convenient |
| 03:28 | hoeck | i'm still wondering for what and when to use metadata, except for docstrings and test-related stuff |
| 03:59 | hoeck | mebaran151: i guess rhickey is going to think about your suggestions when he is up again. |
| 04:00 | hoeck | just found out that find-doc doesn't print the function name :( |
| 04:00 | mebaran151 | I keep the original hash of the reconstructed object and its id in metadata, and using those I can determine whether I should update the object in the db or do nothing |
| 04:00 | mebaran151 | when a call to save is made that is |
| 04:00 | mebaran151 | I hope he does |
| 04:01 | mebaran151 | I think clojure is a pretty cool language |
| 04:01 | mebaran151 | that could use just a little bit more love |
| 04:06 | hoeck | aha, nice |
| 04:07 | hoeck | what do you use for interfacing with the db? |
| 04:22 | mebaran151_ | I'm gonna write that back end part later |
| 04:22 | mebaran151_ | write now |
| 04:22 | mebaran151_ | the filesystem is doing all the heavy lifting |
| 04:24 | mebaran151_ | for this project, I was scared over overflowing int so I decided to make my primary keys vectors of integers which would map to file paths that would each contain buckets of say 128 tiny objects could be slurped up and sliced and diced |
| 04:27 | hoeck | so you're using the clojure reader to read those vectors? |
| 04:27 | Laurent_ | nickname lpetit |
| 04:38 | mebaran151_ | hoeck: currently yes |
| 04:39 | mebaran151_ | I'm planning on supporting a sane way to query with simple key value semantics that can be joined together or orred |
| 04:40 | mebaran151_ | I've never found SQL very easy to grok myself |
| 04:43 | hoeck | mebaran151_: i learned sql by reading the documentation and source at work and by the help of my colleagues |
| 04:43 | mebaran151_ | I know SQL, just it seems so inefficient to produce massive query strings |
| 04:43 | hoeck | but got really enlightened by the relational algebra |
| 04:44 | mebaran151_ | and much of this data is free form, so it doesn't fit in the table metaphor very well |
| 04:45 | mebaran151_ | I know a bit of relation algebra actually. If SQL didn't seem so obsessed with covering up its relation roots |
| 04:45 | mebaran151_ | it might be more sane to code gen it |
| 04:46 | mebaran151_ | and I never thought it really stuck with lisp very well |
| 04:50 | mebaran151_ | anyway I'm going to bed |
| 04:50 | hoeck | but the relational algebra fits good into the lisp world, imho |
| 04:50 | mebaran151_ | it does |
| 04:50 | mebaran151_ | I'll agree with that |
| 04:50 | mebaran151_ | however, far too many problems get solved with SQL aren't really relational in nature |
| 04:51 | hoeck | a algebra->sql compiler is on my todo list :) |
| 04:51 | mebaran151_ | and SQL doesn't do its roots justice I don't think |
| 04:51 | mebaran151_ | it seems as though all the latest database tech is leaving the relational word |
| 04:51 | mebaran151_ | with products like s3 and clouddb and the whole google appdb |
| 04:52 | hoeck | yeah, i've heard of that |
| 04:52 | mebaran151_ | it seems as though relational logic should be done in the language itself |
| 04:52 | mebaran151_ | with conj and disjuct operators on sets |
| 04:52 | hoeck | they are more like big berkeley-dbs |
| 04:53 | mebaran151_ | we're planning on using S3 for some stuff in our app, so it seemed logical to role our own simple query engine |
| 04:53 | hoeck | i'm only working on a small-scale client-server insurance management application |
| 04:54 | mebaran151_ | we're not so big yet ourselves |
| 04:54 | hoeck | (at work) so sql really fits that model |
| 04:54 | hoeck | aha |
| 04:54 | mebaran151_ | sql is a good fit if all your data is going to look the same |
| 04:54 | hoeck | but i guess its hard to roll your own query engine efficiently |
| 04:55 | mebaran151_ | reading up on redblack trees |
| 04:55 | mebaran151_ | which I half way remember how to implement |
| 04:55 | mebaran151_ | there isn't too much magic to SQL systems, esp for single transactions |
| 04:56 | mebaran151_ | you can either loop over all the blocks of the rows in O(n) or hopefully leverage a query and do your work in O(log n) |
| 04:57 | mebaran151_ | it's handling insertion and deletion properly that normally gets ya, but I think Clojure actually provides some pretty powerful primitives along those lines |
| 04:57 | mebaran151_ | *leverage an index |
| 04:59 | mebaran151_ | as none of the individual queries are going to be very complex, i really doubt that there will be too much room for a normal SQL engine to optimize their order, though I could be very ver wrong |
| 05:00 | hoeck | sometimes i'm really surprised how fast a crappy multiline sqlstatement on a long grown database schema can be |
| 05:01 | mebaran151_ | yeah |
| 05:01 | mebaran151_ | is there anything good in clojure world for sql mapping |
| 05:01 | hoeck | okay, for short queries, i guess its the index which is responsible for the speed |
| 05:01 | mebaran151_ | cl lisp had rucksack and I looked at elephant, but didn't find much for clojure |
| 05:02 | hoeck | spring,hibernate :) |
| 05:02 | mebaran151_ | most of the beneift of a big database comes from the fact the daemon can arbitrate queries and optimize the order in which they are received for locaity |
| 05:03 | mebaran151_ | wouldn't hibernate fail though because clojure hashes aren't really classes? |
| 05:03 | hoeck | iirc, there was a simple jdbc->clojure thing on the mailinglist |
| 05:03 | mebaran151_ | seems like hibernate would have to be very Java or at least class oo centric |
| 05:04 | hoeck | i just know hibernate as a buzzword |
| 05:05 | mebaran151_ | I've used it a little bit |
| 05:05 | mebaran151_ | it's pretty pretty fancy |
| 05:07 | hoeck | i never used java before, clojure was my first encounter with java |
| 05:08 | hoeck | but i really like the java libs, though their use seems often over-complicated, once i have something working in clojure, i can abstract the uneccesary java-gluecode stuff away |
| 05:08 | hoeck | so clojure +1! |
| 05:10 | mebaran151_ | I moved from Scala to Clojure |
| 05:10 | mebaran151_ | sometimes I mis the static types |
| 05:11 | hoeck | do you miss them for the speed you gain from static typing? |
| 05:12 | msingh | are places first class in clojure? |
| 05:13 | mebaran151_ | hoeck: not for the speed, but for the way it guided my modeling |
| 05:13 | mebaran151_ | I have to be much more disciplined as I write lisp |
| 05:13 | kotarak | msingh: ehem... What is a place? |
| 05:13 | mebaran151_ | I think clojure does a halfway decent job of coercing types |
| 05:14 | msingh | kotarak: not sure :) |
| 05:14 | mebaran151_ | it's sort of like the difference between installation on a mac and on a PC: it took me forever to get used to the fact that you could just drag files to the Applications folders and everything could just work |
| 05:15 | mebaran151_ | something about static typing makes me feel warm and fuzzy |
| 05:16 | msingh | yes, that's how a straight jacket feels like |
| 05:16 | mebaran151_ | well it does catch some quick bugs and makes sure that your functions interop properly |
| 05:17 | msingh | not an issue |
| 05:17 | kotarak | there is a nice example, where static type checking (with inferencing) caught a infinite loop. |
| 05:17 | msingh | in lisp programming you test your functions as you work on them so those bugs tend to get caught by the programmer as a matter of course |
| 05:18 | mebaran151_ | but for a current project, I want to be able to easily turn some of my functions into xml |
| 05:18 | mebaran151_ | lisp is really really really good for that |
| 05:18 | kotarak | type are another layer to transport information. |
| 05:18 | hoeck | i like the way common-lisp provides types, for having speed where its necessary |
| 05:18 | mebaran151_ | the JVM mostly discards your type info |
| 05:19 | mebaran151_ | most of what I read doesn't really put too much emphasis on types |
| 05:19 | hoeck | yeah, on java, speed isn't such a big issue |
| 05:19 | hoeck | aha |
| 05:19 | mebaran151_ | most of them can be inferred by a smart compiler (aka Hotspot) and fairly efficiently statically inlined |
| 05:23 | hoeck | i wrote a little physics simulation in clojure, and it was as slow as hell :( |
| 05:23 | hoeck | compared to the c++ demo in ran in wine |
| 05:23 | mebaran151_ | hotspot does take awhile to warm up though |
| 05:24 | mebaran151_ | and I don't know much about how well it vectorizes code |
| 05:24 | hoeck | but it was fully dynamic, like adding objects at any time |
| 05:25 | hoeck | well, it used the boxing number types in clojure, and vector-tuples, so it wasn't trimmed on speed either |
| 05:25 | hoeck | and it was way more fun than writing a c++ app :) |
| 05:26 | mebaran151_ | can you tell clojure to downcast if to unboxed primitives if you know best? |
| 05:27 | hoeck | there are some tweaks on unboxed array access, and on using unboxed numbers |
| 05:29 | hoeck | it was discussed in a group-thread about imagej |
| 05:54 | mebaran151_ | I need to get myself on the list... |
| 05:56 | hoeck | clojure moves pretty fast, personally, i read the list and the clojure-log |
| 06:05 | mebaran151_ | because otherwise, I don't see any reason why clojure objects should technically have any slower dispatch once compiled |
| 06:05 | mebaran151_ | but I'm gonna take a na |
| 06:05 | mebaran151_ | p |
| 06:06 | mebaran151_ | good night all |
| 11:47 | jykrd1 | yerrp |
| 11:56 | jykrd1 | I've got clojure to run the ps command, so the output is like: ("UID PID PPID C STIME TTY TIME CMD" "root 1 0 0 Jul25 ? 00:00:02 /sbin/init" "root 2 0 0 Jul25 etc. |
| 11:56 | jykrd1 | but there's no /newlines in the output |
| 11:57 | jykrd1 | it's just one long string. |
| 11:57 | jykrd1 | doesn't ps print a string formated with new lines? |
| 11:57 | kotarak | What you just pasted is a list of strings, each string representing one line. |
| 11:57 | jykrd1 | and tabs |
| 11:57 | rhickey | that looks like a list of strings |
| 11:57 | jykrd1 | oh |
| 11:58 | jykrd1 | so, lines (repeatedly (fn [] (. stdInput readLine))) is creating a list of words, rather than lines, really |
| 11:59 | jykrd1 | er, (let [lines (repeatedly.... etc |
| 11:59 | rhickey | no, it's creating a list of lines, each of which is a string |
| 11:59 | kotarak | I would expect it to produce, what you pasted: a list of lines |
| 12:01 | rhickey | readLine doesn't return the line terminators |
| 12:01 | jykrd1 | right... so should I concatenate a /nl onto lines each line after it returns? |
| 12:01 | rhickey | depends on your goal - what is your goal? |
| 12:02 | jykrd1 | to make a function that can take any shell command and return it like it would in the shell, i guess |
| 12:03 | rhickey | return a single string or print it? |
| 12:03 | rhickey | print directly: (dorun (map println ["a" "b" "c"])) |
| 12:04 | rhickey | print into a string: (with-out-str (dorun (map println ["a" "b" "c"]))) |
| 12:04 | rhickey | replace ["a" "b" "c"] with your list of lines |
| 12:44 | albino | jykrd: fyi, I think the java sigar libs have ps in them if you wanted to call an api instead of forking a process |
| 13:03 | jykrd | albino: thanks. Actually it was just an example. Was wondering if I could use Clojure for shell scripting. some kind of run-command function might help with that. |
| 13:03 | jykrd | seems to work |
| 13:04 | kotarak | Heretic, I know, but: there is also scsh which is kind of specialised on shell scripting, in case you just want something lispish. |
| 13:05 | albino | jykrd: It's a good use case, a lot of what I do is fork processes with my code so I would want some sort of abstraction for that |
| 13:17 | shoover | albino: Just curious, what's the nature of your forking? Is it aimed at fault tolerance or just not sharing state across threads or something else? |
| 13:20 | albino | shoover: getting stuff done on unix requires calling all sorts of command line utilities |
| 13:20 | albino | shoover: for work I write a lot of code that automates the use of our products via command line |
| 13:20 | albino | shoover: In my case it ends up being calls to subprocess.Popen in python |
| 13:24 | shoover | albino: ah, ok, you're talking about good ol' shell scripting. I was getting ahead of myself thinking you were wanting the process abstraction for full-blown Clojure apps. |
| 13:27 | albino | shoover: yep, no api like the command line :) |
| 13:42 | mebaran151 | I'm gonna reiterate my proposal for wiki'izing the docs |
| 13:42 | rhickey | there is a wiki, the docs are a reference |
| 13:43 | mebaran151 | it would be nice if they weren't just a long alphabetical list |
| 13:43 | rhickey | there are many pages of docs in addition to the API list |
| 13:44 | mebaran151 | and I think certain functions could stand nice little docs |
| 13:44 | mebaran151 | *comments |
| 13:45 | mebaran151 | kind of the way php lets users add optional annotations and tips |
| 13:45 | rhickey | link? |
| 13:51 | rhickey | mebaran151: hello? do you have a link for the php wiki you are recommending I emulate? |
| 13:51 | mebaran151 | oh sorry |
| 13:52 | mebaran151 | I have to refind it: I haven't done php in quite awhile but I remember the comment system being superb |
| 13:54 | mebaran151 | http://www.php.net/manual/en/install.macosx.bundled.php |
| 13:54 | mebaran151 | look at the user contributed notes section |
| 13:54 | arohner | postgres has something similar |
| 13:54 | arohner | http://www.postgresql.org/docs/ |
| 13:54 | mebaran151 | postgres I think took the idea from the PHP people |
| 13:55 | arohner | the API section on the website is generated from the docstrings in the code, right? |
| 13:55 | arohner | so it's simple enough to patch that |
| 13:56 | mebaran151 | we could add a more human feel if we did it this way: esp when it comes to little examples for the fancy concurrency goodness that's baked in |
| 13:56 | rhickey | the wikispaces I am using currently has no membership granularity - i.e. all members can edit pages and submit comments. I really don't want people editing pages. I would prefer people use the wiki and build any supplementary material they want. I've asked for examples many times - people will get out of it what they put into it |
| 13:57 | rhickey | I understand the docs are not much of a tutorial |
| 13:58 | mebaran151 | I was pretty new to LISP, but luckily alot of the scheme stuff on the internet helps out |
| 13:58 | rhickey | but there needs to be an accurate reference somewhere |
| 13:59 | rhickey | I've also put up many hours of talks on blip.tv |
| 14:01 | mebaran151 | I've watched them, and they were very informative and what alerted me to clojure in the first place. However, talks are very inefficient when it comes to trying to find specific information. |
| 14:01 | mebaran151 | you can't just search text or skim a talk |
| 14:56 | jykrd | I'd find it useful if someone put examples in the java section of the wiki that described conversion practices, like converting a while loop int clojure |
| 15:30 | jgrant | is clojure a lisp-1 dialect ? |
| 15:31 | kotarak | I think so. |
| 15:31 | kotarak | jgrant: http://clojure.org/lisps has more info on this. |
| 15:32 | jgrant | Really then why is this a problem ? --> |
| 15:32 | jgrant | Clojure |
| 15:32 | jgrant | user=> (defn fact[n] |
| 15:32 | jgrant | (if (= n 0) |
| 15:32 | jgrant | 1 |
| 15:32 | jgrant | (* n (fact (- n 1))))) |
| 15:32 | jgrant | #'user/fact |
| 15:32 | jgrant | user=> (fact 10000) |
| 15:32 | jgrant | java.lang.StackOverflowError |
| 15:32 | jgrant | java.lang.StackOverflowError |
| 15:32 | jgrant | at clojure.lang.Numbers$IntegerOps.equiv(Numbers.java:444) |
| 15:33 | kotarak | Because the jvm has no tail call optimisation |
| 15:33 | kotarak | use loop and recur for such things |
| 15:33 | jgrant | that function does not use tail-recursion |
| 15:34 | jgrant | just simple recursion |
| 15:34 | kotarak | jgrant: then it will even stack overflow on things like scheme |
| 15:34 | jgrant | no you are wrong about that |
| 15:37 | kotarak | jgrant: no I am not. The nice thing about tail recursion is that it runs in constant space (with TCO), recursion as in your example does not since the original context has to be saved. |
| 15:38 | jgrant | recursion in lisp or dialects is idiomatic and fundamental |
| 15:38 | jgrant | it's one of the things that make lisp well.... ...lisp |
| 15:38 | kotarak | yes and every decent text will tell you: use tail recursion. |
| 15:39 | jgrant | really ? which ones ? |
| 15:39 | jgrant | tail recursion is simply an optional compiler optimization |
| 15:39 | kotarak | eg. "teach yourself scheme in fixnum days", IIRC |
| 15:39 | jgrant | it's not required by any implementation |
| 15:40 | kotarak | in scheme it is part of the RxRS |
| 15:41 | jgrant | the code above runs perfectly on mzscheme, scheme48 and a couple others |
| 15:41 | mac__ | kotarak is right, you can't get optimization unless you have the recursive call in a place that the compiler recognizes as tail position, even in other lisps. If you do a non tail recursion that is large enough you will blow the stack. Nothing strange about that, just has different limits (depth) in different languages and runtimes |
| 15:41 | jgrant | also with slight modification on sbcl, allegro, clozure(ccl) and lispworks |
| 15:42 | jgrant | kotarak has changed my initial question |
| 15:42 | jgrant | i'm not asking about tail call optimization |
| 15:42 | kotarak | I did? |
| 15:42 | jgrant | simply recursion |
| 15:42 | mac__ | oh, then use loop/recur to get optimization, otherwise it will be nested calls |
| 15:42 | kotarak | You asked, why your code does not work. And it doesn't work because it uses recursion. |
| 15:43 | kotarak | Even with tail recursion it wouldn't work, because the JVM does not support TCO. |
| 15:43 | jgrant | is it 'my' code that does not 'work' ? |
| 15:44 | mac__ | but loop/recur in clojure "supports TCO" :) so use that? |
| 15:44 | jgrant | that's merely and idiomatic factorial function |
| 15:44 | jgrant | for ANY lisp dialect |
| 15:44 | kotarak | jgrant: sorry. It works. But it has a problem. |
| 15:44 | jgrant | lets try this from another angle... |
| 15:44 | Chouser | jgrant: your example will use 10000 stack frames in very nearly any language. Whether that's enough to cause an exception or not has to do with how deep the stack can get. |
| 15:45 | mac__ | ^^ |
| 15:45 | Chouser | I believe there is a command-line option for java to request a bigger stack. |
| 15:45 | jgrant | Chouser : no it will not |
| 15:46 | jgrant | this is one if the reasons why people choose to use Lisp(or dialects) over stack based languages/vms |
| 15:48 | Chouser | lisp uses a stack. What little CL I know I learned from "on lisp", and it devotes several sections on how to restructure your code so that recursion happens in the tail position. |
| 15:48 | kotarak | SICP (or how it is called) will probably say the same. |
| 15:49 | mac__ | yes and SICP is about scheme, not CL, so that goes for scheme too, at least their version |
| 15:50 | kotarak | TCO is a requirement in the RxRS, the Scheme standard. |
| 15:51 | mac__ | which means it has a stack ;) |
| 15:51 | jgrant | kotarak, Chouser : you are confused about the use of the word 'stack' in 'On Lisp' and 'SICP' |
| 15:51 | jgrant | In 'SICP' page 506 he's referring to the implementation of a stack for the register machine NOT a stack in the underlying implementation of Scheme |
| 15:53 | jgrant | All of CH.5 in 'SICP' covers the design/implentation of a register machine using scheme |
| 15:53 | mac__ | Seems like this discussion has derailed because no matter who is right, it will still blow the stack in clojure since it uses java calls and the jvm does not do TCO so you have to use loop/recur to not blow the stack on deep recursion. |
| 15:54 | mac__ | It's even kinda nice actually since the compiler will tell you when recur is not in tail position so it always get's optimized. |
| 15:54 | jgrant | mac__ : by that logic ANY lisp/scheme implementation should have the same problem because it's implemented in C/C++ |
| 15:54 | jgrant | mac__ : but somehow it does not ;-) |
| 15:56 | jgrant | so it's hard to call clojure a dialect of lisp (even a lisp-1) if simple recursion is not supported |
| 15:56 | mac__ | I was trying to answer you question which was why you couldnt do recursion that way safely in clojure right? The answer is that the jvm uses a stack and you are using too many stack frames with that function and therefore you need to use loop/recur instead or make the stack limit higher. There is no other way that I know of in clojure |
| 15:57 | jgrant | mac__ : this has less to do with the JVM but the actual implementation of clojure |
| 15:58 | mac__ | well yes, but that's part of my point, since clojure uses javas call mechanism it is this way |
| 15:59 | jgrant | mac__ : right and if it does then you have hit it on the head as to why that's the problem |
| 16:00 | jgrant | mac__ : and why clojure is not a dialect of lisp at all (but does has some lisp features in syntax, macros) |
| 16:00 | mac__ | Well, I'm not good enough at all this to answer why rich did it that way, you need to ask him. I think I've seen a discussion about this on the google group, start by searching there |
| 16:00 | jgrant | mac__ : I would love to hear Rich's response on this, could you point me to it somewhere ? |
| 16:00 | kotarak | I think it was something about Java interaction. |
| 16:00 | Chouser | it doesn't blow the JVM operator stack (which is used instead of registers), it blows the JVM's call stack, which all these languages use. |
| 16:01 | mac__ | here is the group, don't recall the discussion topic though, use the search field |
| 16:01 | mac__ | http://groups.google.com/group/clojure |
| 16:02 | jgrant | Thanks |
| 16:03 | jgrant | So according to Rich Clojure will never be a lisp dialect but simply some type of functional language |
| 16:03 | jgrant | Clojure could have been implemented with a heap-based stack, with a |
| 16:03 | jgrant | cost in performance and call-compatibility with Java. I chose not to |
| 16:03 | jgrant | do so, and am very happy with the results. It's not an aspect of |
| 16:03 | jgrant | Clojure that is going to change. |
| 16:03 | jgrant | Sorry the last 5 lines are a quote |
| 16:05 | jgrant | and |
| 16:05 | jgrant | RIch : As such, it has its own set of idioms. |
| 16:05 | mac__ | there you go, it's for performance and java interop, good reasons IMO, you get used to loop/recur pretty fast |
| 16:06 | jgrant | and that's fine I agree completely that not all functional languages will follow all of Lisps features BUT it's these features of Lisp that make it |
| 16:06 | jgrant | and Clojure should not be referred to as a Lisp by any stretch |
| 16:07 | jgrant | It's definitely not a lisp-1 |
| 16:09 | jgrant | The very first paper by McCarthy in 1960 was titled "Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I" |
| 16:10 | jgrant | which detailed Lisp's design |
| 16:13 | jgrant | The very first 2 implementations by Russell (in assembly language) supported recursion |
| 16:13 | arohner | what does lisp-1 vs. lisp-2 have to do with recursion? |
| 16:14 | mac__ | well if you absolutely insist that recursion must be done using the original name of the function then you can avoid stack blowage by using a lazy sequence in clojure, look up lazy-cons. I don't see you point at all. |
| 16:14 | jgrant | It cannot even be done with (recur ...) in clojure , you are forced to implement it as tail-recursive |
| 16:14 | mac__ | if you can write a function that looks like a recursive function but compiles to an iterative loop, why do you need to care? |
| 16:14 | jgrant | what about n-recursive functions ? |
| 16:16 | jgrant | So fine Clojure is a functional language |
| 16:16 | jgrant | but please do not keep calling it a Lisp (see the headline of this room above) |
| 16:37 | shoover | that's an interesting definition of lisp. McCarthy's paper just says functions can call themselves, with no stated depth requirement. Norvig and Pitman say not to use recursion for iteration (http://www.cs.umd.edu/~nau/cmsc421/norvig-lisp-style.pdf). And a simple emacs lisp function just blew up at a depth of 347. |
| 16:38 | kotarak | Since the state of the function has to be saved, it doesn't matter whether you have a stack or not. At some point in time you will run out of space. With a tail call and TCO the function runs in constants space. So this cannot happen. |
| 16:38 | shoover | Ah, that too |
| 16:40 | mac__ | yeah, jgrant was weird. I always thought what made lisp lisp is the combination of read/eval and sexp/macros |
| 16:40 | mac__ | that's pretty much the only thing still unique to lisp |
| 16:41 | kotarak | I actually don't care. Lisp is Lisp. Clojure is Clojure. Scheme is Scheme. And at least the latter two are fun. :) |
| 16:42 | mac__ | yeah I don't understand why he made a big deal about it |
| 16:44 | shoover | This all speaks to the accuracy of Clojure's tagline. Please don't change it |
| 17:19 | albino | Is someone here writingcode.blogspot.com ? |
| 17:47 | Chouser | well it's a little unfair to challenge jgrant's statements while he's not here, but I don't want to leave them unchallenged. |
| 17:48 | Chouser | If I understood him correctly, the following Common Lisp function when called will run forever but not crash: (defun i () (+ 1 (i))) |
| 17:48 | kotarak | That was his claim. |
| 17:48 | Chouser | Executing that in Steel Bank Common Lisp, I get "Control stack exhausted (no more space for function call frames). This is probably due to heavily nested or infinitely recursive function calls." |
| 17:49 | kotarak | Surprise. |
| 17:50 | Chouser | Similarly in clojure, (defn i [] (+ 1 (i))) produces java.lang.StackOverflowError |
| 17:50 | kotarak | It doesn't matter whether there is a stack involved or not. The machine has only finite memory to save function contexts. |
| 17:50 | Chouser | (when I run (i)) |
| 17:50 | kotarak | There was some explanation like this: |
| 17:51 | kotarak | (fac n) |
| 17:51 | kotarak | (* n (fac (- n 1))) |
| 17:51 | Chouser | If I put the lisp recursion in tail position, like this (defun i () (i)) then indeed SPCL seems to run indefinitely without crashing. |
| 17:51 | kotarak | (* n (* n-1 (fac (- n-1 1))) |
| 17:51 | kotarak | ---> grows into that direction |
| 17:51 | Chouser | kotarak: yup |
| 17:51 | kotarak | (fac n 1) |
| 17:52 | kotarak | (fac n-1 n) |
| 17:52 | kotarak | (fac n-2 n*n-1) |
| 17:52 | Chouser | The equivalent clojure also runs indefinitely without crashing: (defn i [] (recur)) |
| 17:52 | kotarak | stays constant |
| 17:54 | kotarak | I think he got something terribly wrong. And it shows that prove-by-experiment doesn't really work. |
| 17:56 | scgilardi | although disprove by experiment works fine. a "simple" recursive factorial in DrScheme with a memory limit of 128MB runs out of memory at some value under a million. |
| 17:58 | kotarak | "prove" by counter-example, yes. But he was some kind of advice-resistent. |
| 17:59 | arbscht | proof by experiment doesn't always work, then. it does really work sometimes, though |
| 18:01 | arbscht | and that was the most puzzling definition of lisp I've encountered |
| 18:18 | rhickey | sorry I missed jgrant, here are the facts: |
| 18:19 | rhickey | Lisp-1 has nothing to do with TCO, it has to do with whether functions have an independent namespace. Clojure is like Scheme in that they don't, so both are Lisp-1s. |
| 18:19 | rhickey | Supporting recursion has nothing to do with TCO. All Lisps support recursion (a function calling itself), and so does Clojure |
| 18:20 | rhickey | Scheme requires TCO, Common Lisp does not. Common Lisps are obviously Lisps, and so is Clojure |
| 18:20 | rhickey | Clojure is not a Scheme |
| 18:24 | Chouser | rhickey: we're all sorry you missed jgrant :-) |
| 19:46 | abrooks | jgrant: There was some reply to your Clojure as Lisp inquiry: http://clojure-log.n01se.net/date/2008-07-26.html |
| 19:46 | abrooks | jgrant: You'd probably be best scrolling down to the bottom. |
| 22:27 | jgrant | abrooks : thanks I see rich's response |
| 22:28 | jgrant | not sure why this became a discussion about TCO |
| 22:28 | jgrant | my initial question had to do with simple recursion |
| 22:29 | jgrant | and rich's response that i quoted earlier today already provided the answer --> |
| 22:30 | jgrant | http://groups.google.com/group/clojure/browse_thread/thread/72a7fa0fe5a56c9c/e3849c42b21551c0?lnk=gst&q=recursion#e3849c42b21551c0 |
| 22:30 | jgrant | "Clojure could have been implemented with a heap-based stack, with a" |
| 22:30 | jgrant | cost in performance and call-compatibility with Java. I chose not to |
| 22:30 | jgrant | do so, and am very happy with the results. It's not an aspect of |
| 22:30 | jgrant | Clojure that is going to change. " |
| 22:31 | jgrant | it was a design choice to not allow recursion until until the heap allocated memory is exhausted |
| 22:31 | jgrant | and that's fine |
| 22:32 | jgrant | As I've stated here and elsewhere I think Clojure is great, it's probably my most favorite language on the JVM |
| 22:32 | jgrant | thanks again Rich |
| 22:38 | jgrant | As to my personal opinion about why it's not really a Lisp... |
| 22:38 | jgrant | ...from McCarthy's original paper in 1960 : |
| 22:39 | jgrant | (in the introduction) |
| 22:39 | jgrant | http://www-formal.stanford.edu/jmc/recursive/node1.html |
| 22:40 | jgrant | It's clearly stated what Lisp was intended to be : |
| 22:40 | jgrant | "In this article, we first describe a formalism for defining functions recursively." |
| 22:41 | jgrant | only after that came s-expressions and s-functions and thirdly an example of the s-function 'apply' |
| 22:42 | jgrant | The paper itself is titled "Recursive Functions of Symbolic Expressions and Their Computation by Machine, Part I" |
| 22:42 | jgrant | This is just my opinion as calling Clojure a dialect of Lisp |
| 22:45 | jgrant | however it's definitely a functional language |
| 23:27 | dudleyf | jgrant: Are you claiming that Clojure doesn't support "defining functions recursively"? |
| 23:27 | Chouser | If you exclude TCO from the conversation, Clojure supports recursion just as well as Common Lisp does, as my SBCL exmples help demonstarte. |
| 23:31 | Chouser | dudleyf's question is better than my assertion. Let's go with that. :-) |