* Avoiding shared data @ 2005-09-25 21:32 Martin Chabr 2005-09-26 0:23 ` [Caml-list] " Bill Wood ` (2 more replies) 0 siblings, 3 replies; 44+ messages in thread From: Martin Chabr @ 2005-09-25 21:32 UTC (permalink / raw) To: caml-list Working with non-shared data structures in OCaml Deep copy of OCaml structures, marshaling ================================================ Dear group members, I need to process arrays of pairs of integers and records ((int * record) array) in which all elements must be updated individually, which means that unshared data structures must be used. For arrays of arrays I can produce unshared data by using the library functions Array.copy and Array.append to append the individual arrays into the embedding array. It works, the low level arrays can be updated individually. But I cannot use the same scheme to the array of the (int * record) structures, because I do not know how to copy these structures to dissolve the sharing. I do not even know how to copy records. It seems to me that this problem occurs always when I want to produce an array of data with a fixed structure automatically (rather than entering the array [| ... |] by hand at the top level interpreter using constants only). How can I produce completely unshared structures? What about marshaling and unmarshaling the data? This should produce a deep copy of data objects. I have tried it, it works, but it seems to me wasteful to copy all the data twice only to get rid of the sharing. It would be great to know of a completely general (any nested structures) and fast solution (without copying) how to produce unshared data structures. My environment is OCaml 3.08.02 for Windows on Win 2000, New Windows Interface v1.9RC4. I am looking forward to you reply. Regards, Martin Martin Chabr Hochstrasse 28 8044 Zürich Schweiz / Switzerland Tel.P.: 01-261 17 24 ___________________________________________________________ Was denken Sie über E-Mail? Wir hören auf Ihre Meinung: http://surveylink.yahoo.com/wix/p0379378.aspx ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] Avoiding shared data 2005-09-25 21:32 Avoiding shared data Martin Chabr @ 2005-09-26 0:23 ` Bill Wood 2005-09-26 7:57 ` Claudio Sacerdoti Coen 2005-09-26 8:17 ` William Lovas 2 siblings, 0 replies; 44+ messages in thread From: Bill Wood @ 2005-09-26 0:23 UTC (permalink / raw) To: Martin Chabr; +Cc: caml-list . . . > I need to process arrays of pairs of integers and > records ((int * record) array) in which all elements > must be updated individually, which means that > unshared data structures must be used. For arrays of > arrays I can produce unshared data by using the > library functions Array.copy and Array.append to > append the individual arrays into the embedding array. > It works, the low level arrays can be updated > individually. But I cannot use the same scheme to the > array of the (int * record) structures, because I do > not know how to copy these structures to dissolve the > sharing. I do not even know how to copy records. It > seems to me that this problem occurs always when I > want to produce an array of data with a fixed > structure automatically (rather than entering the > array [| ... |] by hand at the top level interpreter > using constants only). How can I produce completely > unshared structures? Funny you should mention it, I wrestled with this just this week. I'm building a square, i.e. array of N elements, each of which is itself an array of N cells, where a cell is a record with several fields. Here's the way I did it: 1) I defined a function initial_cell : unit -> cell as let initial_cell () = let c = {field1 = val1; ...; fieldN = valN} in c;; 2) I defined a function initial_row : int -> cell array as: let initial_row n = Array.init n (fun i -> initial_cell ());; 3) I defined a function initial_square : int -> cell array array by: let initial_square n = Array.init n (fun i -> initial_row ());; I think the essential point is the Array.init function; since it expects to set the i-th element of the new array to (f i) for unknown f, it can't make an array that shares. I hope this is of some help, -- Bill Wood bill.wood@acm.org ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] Avoiding shared data 2005-09-25 21:32 Avoiding shared data Martin Chabr 2005-09-26 0:23 ` [Caml-list] " Bill Wood @ 2005-09-26 7:57 ` Claudio Sacerdoti Coen 2005-09-26 8:17 ` William Lovas 2 siblings, 0 replies; 44+ messages in thread From: Claudio Sacerdoti Coen @ 2005-09-26 7:57 UTC (permalink / raw) To: caml-list > It would be great to know of a completely general (any > nested structures) and fast solution (without copying) > how to produce unshared data structures. If you like to play with fire you can use this unsharing function (that is not complete with respect to any data type, but it is sufficient for my needs). Since it works on the run-time representation of data, it can even unshare abstract data types. exception CanNotUnshare;; (* [unshare t] gives back a copy of t where all sharing has been removed *) (* Physical equality becomes meaningful on unshared terms. Hashtables that *) (* use physical equality can now be used to associate information to evey *) (* node of the term. *) val unshare: ?already_unshared:('a -> bool) -> 'a -> 'a let unshare ?(already_unshared = function _ -> false) t = let obj = Obj.repr t in let rec aux obj = if already_unshared (Obj.obj obj) then obj else (if Obj.is_int obj then obj else if Obj.is_block obj then begin let tag = Obj.tag obj in if tag < Obj.no_scan_tag then begin let size = Obj.size obj in let new_obj = Obj.new_block 0 size in Obj.set_tag new_obj tag ; for i = 0 to size - 1 do Obj.set_field new_obj i (aux (Obj.field obj i)) done ; new_obj end else if tag = Obj.string_tag then obj else raise CanNotUnshare end else raise CanNotUnshare ) in Obj.obj (aux obj) ;; -- ---------------------------------------------------------------- Real name: Claudio Sacerdoti Coen Doctor in Computer Science, University of Bologna E-mail: sacerdot@cs.unibo.it http://www.cs.unibo.it/~sacerdot ---------------------------------------------------------------- ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] Avoiding shared data 2005-09-25 21:32 Avoiding shared data Martin Chabr 2005-09-26 0:23 ` [Caml-list] " Bill Wood 2005-09-26 7:57 ` Claudio Sacerdoti Coen @ 2005-09-26 8:17 ` William Lovas 2005-09-26 21:07 ` Ant: " Martin Chabr 2 siblings, 1 reply; 44+ messages in thread From: William Lovas @ 2005-09-26 8:17 UTC (permalink / raw) To: caml-list; +Cc: Martin Chabr Hi Martin, On Sun, Sep 25, 2005 at 11:32:02PM +0200, Martin Chabr wrote: > [...] But I cannot use the same scheme to the > array of the (int * record) structures, because I do > not know how to copy these structures to dissolve the > sharing. I do not even know how to copy records. > [...] How can I produce completely > unshared structures? Maybe i'm missing something, but if these are unmutable records, then why do you need to concern yourself with any potential sharing? As long as the array cells are not "shared" -- which they can't be, as far as i know -- you can update each one individually no matter what the sharing status of their contents is. If the records *are* mutable, then the suggestion to use Array.init should be sufficient. Hoping i might save you some work :) William ^ permalink raw reply [flat|nested] 44+ messages in thread
* Ant: Re: [Caml-list] Avoiding shared data 2005-09-26 8:17 ` William Lovas @ 2005-09-26 21:07 ` Martin Chabr 2005-09-26 22:08 ` Jon Harrop 2005-09-30 22:57 ` Oliver Bandel 0 siblings, 2 replies; 44+ messages in thread From: Martin Chabr @ 2005-09-26 21:07 UTC (permalink / raw) To: William Lovas; +Cc: caml-list Hello William, I am using a mutable record. I am programming this 90% in the imperative (non-functional) style, so that I can rewrite critical parts into Fortran easily. Another reason is, I am an intermediate user and finding out whether the recursion is a tail-one or not is difficult for me. This is a kind of number crunching problem and the data structures will be huge. Blessed are the creators of OCaml for the inclusion of all imperative constructs. Martin --- William Lovas <wlovas@stwing.upenn.edu> schrieb: > Hi Martin, > > On Sun, Sep 25, 2005 at 11:32:02PM +0200, Martin > Chabr wrote: > > [...] But I cannot use the same scheme to the > > array of the (int * record) structures, because I > do > > not know how to copy these structures to dissolve > the > > sharing. I do not even know how to copy records. > > [...] How can I produce completely > > unshared structures? > > Maybe i'm missing something, but if these are > unmutable records, then why > do you need to concern yourself with any potential > sharing? As long as the > array cells are not "shared" -- which they can't be, > as far as i know -- > you can update each one individually no matter what > the sharing status of > their contents is. > > If the records *are* mutable, then the suggestion to > use Array.init should > be sufficient. > > Hoping i might save you some work :) > > William > Martin Chabr Hochstrasse 28 8044 Zürich Schweiz / Switzerland Tel.P.: 01-261 17 24 ___________________________________________________________ Gesendet von Yahoo! Mail - Jetzt mit 1GB Speicher kostenlos - Hier anmelden: http://mail.yahoo.de ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: Ant: Re: [Caml-list] Avoiding shared data 2005-09-26 21:07 ` Ant: " Martin Chabr @ 2005-09-26 22:08 ` Jon Harrop 2005-09-30 22:57 ` Oliver Bandel 1 sibling, 0 replies; 44+ messages in thread From: Jon Harrop @ 2005-09-26 22:08 UTC (permalink / raw) To: caml-list On Monday 26 September 2005 22:07, Martin Chabr wrote: > I am using a mutable record. I am programming this 90% > in the imperative (non-functional) style, so that I > can rewrite critical parts into Fortran easily. In case you haven't already - I'd advise you to write a simple working version first. -- Dr Jon D Harrop, Flying Frog Consultancy Ltd. Objective CAML for Scientists http://www.ffconsultancy.com/products/ocaml_for_scientists ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: Ant: Re: [Caml-list] Avoiding shared data 2005-09-26 21:07 ` Ant: " Martin Chabr 2005-09-26 22:08 ` Jon Harrop @ 2005-09-30 22:57 ` Oliver Bandel 2005-10-01 0:07 ` Pal-Kristian Engstad 2005-10-01 21:36 ` Ant: Re: Ant: " Martin Chabr 1 sibling, 2 replies; 44+ messages in thread From: Oliver Bandel @ 2005-09-30 22:57 UTC (permalink / raw) To: caml-list On Mon, Sep 26, 2005 at 11:07:30PM +0200, Martin Chabr wrote: > Hello William, > > I am using a mutable record. I am programming this 90% > in the imperative (non-functional) style, so that I > can rewrite critical parts into Fortran easily. > Another reason is, I am an intermediate user and > finding out whether the recursion is a tail-one or not > is difficult for me. When you 90% of your code are writing in imperative style and do not go deeper into the functional/recursive world, you will never be able to distinguish between tail-rec and non-tail-rec style. But: It is not really hard to find the distinction betwen the two styles, but often the explanations are not made well. Sometimes it's only one or two words in an explanation about tail-rec/non-tail-rec that must be substituted by other words, and the distinction can be made visible very easy. On the other hand: writing mor funtional/recursive code will make you more used to to this... Ciao, Oliver ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: Ant: Re: [Caml-list] Avoiding shared data 2005-09-30 22:57 ` Oliver Bandel @ 2005-10-01 0:07 ` Pal-Kristian Engstad 2005-10-01 5:46 ` Bill Wood ` (3 more replies) 2005-10-01 21:36 ` Ant: Re: Ant: " Martin Chabr 1 sibling, 4 replies; 44+ messages in thread From: Pal-Kristian Engstad @ 2005-10-01 0:07 UTC (permalink / raw) To: caml-list; +Cc: Oliver Bandel On Friday 30 September 2005 03:57 pm, Oliver Bandel wrote: > On the other hand: writing mor funtional/recursive code will > make you more used to to this... I've always thought that this was a really bad argument from the ML camp. The logic of complicated control-paths is very easily made a zillion times worse by writing in a tail-recursive style. It is *not* a good programming practice to make hard-to-read code! I encourage people to read the paper by Olin Shivers: "The Anatomy of a Loop - A story of scope and control", which was presented at ICFP 2005, and can be found at http://www.cc.gatech.edu/~shivers/papers/loop.pdf. The author argues that "Writing loops with tail-recursive function calls is the equivalent of writing them with goto’s." and gives an example that I've rewritten from Scheme-ish into OCaml-ish: let myfunc l = let rec loop rest result = match rest with | [] -> List.rev result | x::xs -> if xpred x then let y = verbose_code_using_x x in if ypred y then let z = verbose_code_using_y y in loop xs (z_expression :: result) else loop xs result else loop xs result in loop l [] ;; Obviously, one would like to refactor this into HOF, but in this situation it is hard to see how one should do it. The author instead proposes to use loops in a monadic style, which I've again rewritten: let myfunc l = loop [ for x in l ; when xpred x ; let y = verbose_code_using_x x ; when ypred y ; let z = verbose_code_using_y y ; save z ] The point being (syntax aside) that this code is much more readible, easier to change and easier to verify than the code given above. [Of course, Haskell has the really cool list-comprehension syntax that alleviates some of ML's problems, but still.] Thanks, PKE. -- _ \`. Pål-Kristian Engstad, Lead Programmer, \ `| Naughty Dog, Inc., 1601 Cloverfield Blvd, 6000 North, __\ |`. Santa Monica, CA 90404, USA. (310) 633-9112. / /o mailto:engstad@naughtydog.com http://www.naughtydog.com / '~ mailto:mrengstad@yahoo.com http://www.engstad.com / ,' Hang-gliding Rulez! ~' ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-01 0:07 ` Pal-Kristian Engstad @ 2005-10-01 5:46 ` Bill Wood 2005-10-01 8:27 ` Wolfgang Lux ` (2 subsequent siblings) 3 siblings, 0 replies; 44+ messages in thread From: Bill Wood @ 2005-10-01 5:46 UTC (permalink / raw) To: caml-list . . . > I've always thought that this was a really bad argument from the ML camp. The > logic of complicated control-paths is very easily made a zillion times worse > by writing in a tail-recursive style. It is *not* a good programming practice > to make hard-to-read code! > > I encourage people to read the paper by Olin Shivers: "The Anatomy of a Loop - > A story of scope and control", which was presented at ICFP 2005, and can be > found at http://www.cc.gatech.edu/~shivers/papers/loop.pdf. > > The author argues that "Writing loops with tail-recursive function calls is > the equivalent of writing them with goto’s." and gives an example that I've > rewritten from Scheme-ish into OCaml-ish: > > let myfunc l = > let rec loop rest result = > match rest with > | [] -> List.rev result > | x::xs -> > if xpred x then > let y = verbose_code_using_x x in > if ypred y then > let z = verbose_code_using_y y in > loop xs (z_expression :: result) > else > loop xs result > else > loop xs result > in > loop l [] > ;; > > Obviously, one would like to refactor this into HOF, but in this situation it > is hard to see how one should do it. The author instead proposes to use loops > in a monadic style, which I've again rewritten: > > let myfunc l = > loop [ for x in l > ; when xpred x > ; let y = verbose_code_using_x x > ; when ypred y > ; let z = verbose_code_using_y y > ; save z > ] > > The point being (syntax aside) that this code is much more readible, easier to > change and easier to verify than the code given above. [Of course, Haskell > has the really cool list-comprehension syntax that alleviates some of ML's > problems, but still.] I about half agree with Mr. Engstad's observation. I have often used the fact that the accumulator passed around in tail-recursive functions acts like a state (some people call these *state threads*), and I've used techniques like pushing Dijkstra's weakest-precondition predicate transformers over accumulators to reason about tail-recursive functional code almost as if it were imperative (the only difference with Mr. Shivers is that my programming is goto-less :-). However, the example is a little unfortunate in that transforming it to an arguably cleaner HOF form is fairly easy. If we first define function composition as an operator (shouldn't this be an OCaml Pervasive?), say let ($@) fyz fxy x = fyz (fxy x);; then the tail-recursive version of myfunc transforms into my_hof_func: let my_hof_func l = let fumble = let save = rev $@ (fold_left (fun la i -> i :: la) []) in save $@ (map verbose_code_using_y) $@ (filter ypred) $@ (map verbose_code_using_x) $@ (filter xpred) in fumble l;; where packaging and return of the result has been encapsulated in the local function 'save' (I'm also assuming z_expression was supposed to be z). A minor problem is that the textual order of the predicates and functions is reversed. Even this can be handled by a sleazy trick -- reverse the order of the operands to the compose operator like so: let ($@) fxy fyz x = fyz (fxy x);; We can then rewrite my_hof_func as let my_hof_func l = let fumble = let save = (fold_left (fun la i -> i :: la) []) $@ rev in (filter xpred) $@ (map verbose_code_using_x) $@ (filter ypred) $@ (map verbose_code_using_y) $@ save in fumble l;; The actions/functions are read in the order they are performed/applied, and trivial reformatting even makes it look sorta like imperative code. A very similar transformation could be done on the "Scheme-ish" original, where a variable-arity form (compose f1 ... fn) is used to equally good effect. I think the ease-of-reading issue can now be revisited on a slightly more even playing field. The transformed, HOF, code is a couple of lines longer, but the meaning is fairly transparent because native OCaml facilities are used throughout. The monadic form above is a trifle shorter, but has the liability that the new syntax has complex semantics -- what *exactly* is going on with "when" and "save"?. I note also that the monadic form looks very similar to the infamous Common Lisp "loop" facility (the one towards the back of The Book, with all the keywords). Many lispers love it, and many loathe it. Does anybody know if MLers have looked at either the Series or the Generators-and-Gatherers described in Appendices A and B of Common Lisp the Language, 2nd ed.? Some of the ideas there look interesting. Interesting topic, -- Bill Wood bill.wood@acm.org ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-01 0:07 ` Pal-Kristian Engstad 2005-10-01 5:46 ` Bill Wood @ 2005-10-01 8:27 ` Wolfgang Lux 2005-10-01 18:02 ` Wolfgang Lux 2005-10-01 21:50 ` Ant: " Martin Chabr 2005-10-01 12:34 ` Oliver Bandel 2005-10-01 21:05 ` Ant: " Martin Chabr 3 siblings, 2 replies; 44+ messages in thread From: Wolfgang Lux @ 2005-10-01 8:27 UTC (permalink / raw) To: Pal-Kristian Engstad; +Cc: caml-list, Oliver Bandel Pal-Kristian Engstad wrote: > The author argues that "Writing loops with tail-recursive function > calls is > the equivalent of writing them with goto’s." and gives an example that > I've > rewritten from Scheme-ish into OCaml-ish: > > let myfunc l = > let rec loop rest result = > match rest with > | [] -> List.rev result > | x::xs -> > if xpred x then > let y = verbose_code_using_x x in > if ypred y then > let z = verbose_code_using_y y in > loop xs (z_expression :: result) > else > loop xs result > else > loop xs result > in > loop l [] > ;; > > Obviously, one would like to refactor this into HOF, but in this > situation it > is hard to see how one should do it. Oh come on! IMHO, most loops can easily be transformed by using map, filter, fold_left, and fold_right. The example given is no exception. The loop essentially applies some complex code to every element of the list and includes the result in the output list only if a condition is satisfied that is computed along with result. This is always the structure of a fold. (If the condition were independent from the result one could combine a filter and a map.) Thus, a straight forward rewrite of the loop is: let myfunc l = let f x result = if xpred then let y = verbose_code_using_x x in if ypred y then let z = verbose_code_using_y y in z_expression :: result else result else result in fold_right f l [] Furthermore, I would turn the two if expressions into local functions let myfunc l = let g y result = if ypred y then let z = verbose_code_using_y y in z_expression :: result else result in let f x result = if xpred x then let y = verbose_code_using_x x in g y result else result in fold_right f l [] Regards Wolfgang ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-01 8:27 ` Wolfgang Lux @ 2005-10-01 18:02 ` Wolfgang Lux 2005-10-01 21:50 ` Ant: " Martin Chabr 1 sibling, 0 replies; 44+ messages in thread From: Wolfgang Lux @ 2005-10-01 18:02 UTC (permalink / raw) To: Wolfgang Lux; +Cc: Pal-Kristian Engstad, caml-list, Oliver Bandel Wolfgang Lux wrote: > let myfunc l = > let g y result = > if ypred y then > let z = verbose_code_using_y y in z_expression :: result > else > result > in let f x result = > if xpred x then > let y = verbose_code_using_x x in g y result > else > result > in fold_right f l [] Just correcting myself. The code should use fold_left rather than fold_right (and thus switch the parameters in the call as well as that of the local functions f and g) because the former is tail recursive and therefore should be preferred in a strict language (have been programming too much Haskell lately where a right fold is better). Wolfghang ^ permalink raw reply [flat|nested] 44+ messages in thread
* Ant: Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-01 8:27 ` Wolfgang Lux 2005-10-01 18:02 ` Wolfgang Lux @ 2005-10-01 21:50 ` Martin Chabr 1 sibling, 0 replies; 44+ messages in thread From: Martin Chabr @ 2005-10-01 21:50 UTC (permalink / raw) To: Wolfgang Lux; +Cc: caml-list Hello Wolfgang, you are quite right, most loops can easily be transformed by using map, filter, fold_left or fold_right. I just tend to forget about it or find it uneasy if the problem is more complex. It may be just a matter of habit. Regards, Martin --- Wolfgang Lux <wlux@uni-muenster.de> schrieb: > Pal-Kristian Engstad wrote: > > > The author argues that "Writing loops with > tail-recursive function > > calls is > > the equivalent of writing them with gotos." and > gives an example that > > I've > > rewritten from Scheme-ish into OCaml-ish: > > > > let myfunc l = > > let rec loop rest result = > > match rest with > > | [] -> List.rev result > > | x::xs -> > > if xpred x then > > let y = verbose_code_using_x x in > > if ypred y then > > let z = verbose_code_using_y y in > > loop xs (z_expression :: result) > > else > > loop xs result > > else > > loop xs result > > in > > loop l [] > > ;; > > > > Obviously, one would like to refactor this into > HOF, but in this > > situation it > > is hard to see how one should do it. > > Oh come on! IMHO, most loops can easily be > transformed by using map, > filter, > fold_left, and fold_right. The example given is no > exception. The loop > essentially applies some complex code to every > element of the list and > includes the result in the output list only if a > condition is satisfied > that is computed along with result. This is always > the structure of a > fold. > (If the condition were independent from the result > one could combine a > filter > and a map.) Thus, a straight forward rewrite of the > loop is: > > let myfunc l = > let f x result = > if xpred then > let y = verbose_code_using_x x in > if ypred y then > let z = verbose_code_using_y y in > z_expression :: result > else > result > else > result > in > fold_right f l [] > > Furthermore, I would turn the two if expressions > into local functions > > let myfunc l = > let g y result = > if ypred y then > let z = verbose_code_using_y y in > z_expression :: result > else > result > in let f x result = > if xpred x then > let y = verbose_code_using_x x in g y > result > else > result > in fold_right f l [] > > Regards > Wolfgang > > _______________________________________________ > Caml-list mailing list. Subscription management: > http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list > Archives: http://caml.inria.fr > Beginner's list: > http://groups.yahoo.com/group/ocaml_beginners > Bug reports: http://caml.inria.fr/bin/caml-bugs > ___________________________________________________________ Gesendet von Yahoo! Mail - Jetzt mit 1GB Speicher kostenlos - Hier anmelden: http://mail.yahoo.de ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-01 0:07 ` Pal-Kristian Engstad 2005-10-01 5:46 ` Bill Wood 2005-10-01 8:27 ` Wolfgang Lux @ 2005-10-01 12:34 ` Oliver Bandel 2005-10-01 13:58 ` Bill Wood 2005-10-01 21:05 ` Ant: " Martin Chabr 3 siblings, 1 reply; 44+ messages in thread From: Oliver Bandel @ 2005-10-01 12:34 UTC (permalink / raw) To: caml-list On Fri, Sep 30, 2005 at 05:07:00PM -0700, Pal-Kristian Engstad wrote: > On Friday 30 September 2005 03:57 pm, Oliver Bandel wrote: > > On the other hand: writing mor funtional/recursive code will > > make you more used to to this... > > I've always thought that this was a really bad argument from the ML camp. It is not a bad argument from the ML camp. It's always so, that if you have more practice you will be more used to something and you learn it better. If you don't practise, learning is abandoned. This has nothing to do with programming languages. [...] > The > logic of complicated control-paths is very easily made a zillion times worse > by writing in a tail-recursive style. It is *not* a good programming practice > to make hard-to-read code! Some things are better wriiten down functionally, others are better suited for using impoerative code. Since I get more and more used to using functional and recursive code writing, I can better decide, which way is better. And more and more often I decide to use the recursive style. In OCaml you are not restricted to it, but if you only use one style of programming, and never practise the other programming styles, you can't see, when which programming style/paradigm is better, and the original poster said something about the distinction of tail-rec vs. non tail-rec. (It was not about if that style makes sense or not.) If you practise more of that stuff, and reading some good explanations, then it's obvious, which solution is tail-rec and which is not. > > I encourage people to read the paper by Olin Shivers: "The Anatomy of a Loop - > A story of scope and control", which was presented at ICFP 2005, and can be > found at http://www.cc.gatech.edu/~shivers/papers/loop.pdf. Thanks for the link. > > The author argues that "Writing loops with tail-recursive function calls is > the equivalent of writing them with goto???s." I doubt that the author writes that. You mean for/while instead of goto's as a substitute for recursive functions...?! Ciao, Oliver ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-01 12:34 ` Oliver Bandel @ 2005-10-01 13:58 ` Bill Wood 0 siblings, 0 replies; 44+ messages in thread From: Bill Wood @ 2005-10-01 13:58 UTC (permalink / raw) To: caml-list . . . > > The author argues that "Writing loops with tail-recursive function calls is > > the equivalent of writing them with goto???s." > > I doubt that the author writes that. > You mean for/while instead of goto's as a substitute for > recursive functions...?! Actually, the author does say that. More precisely, Shivers refers to Steele's characterization of lambda as "gotos with arguments"; I think the source is the paper "Lambda: the Ultimate GOTO". -- Bill Wood bill.wood@acm.org ^ permalink raw reply [flat|nested] 44+ messages in thread
* Ant: Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-01 0:07 ` Pal-Kristian Engstad ` (2 preceding siblings ...) 2005-10-01 12:34 ` Oliver Bandel @ 2005-10-01 21:05 ` Martin Chabr 2005-10-03 0:41 ` skaller 3 siblings, 1 reply; 44+ messages in thread From: Martin Chabr @ 2005-10-01 21:05 UTC (permalink / raw) To: Pal-Kristian Engstad; +Cc: caml-list Hello Pal-Kristian, I agree with you that functional code written in a tail recursive style is hard to read. Sometimes you have to do it that way if you want to avoid a stack overflow. I hope that one day functional language compilers will do that optimization for you - convert a non-tail-recursive code into a tail-recursive one. Do you know of some progress in that direction? Regards, Martin --- Pal-Kristian Engstad <pal_engstad@naughtydog.com> wrote: > I've always thought that this was a really bad > argument from the ML camp. The > logic of complicated control-paths is very easily > made a zillion times worse > by writing in a tail-recursive style. It is *not* a > good programming practice > to make hard-to-read code! ___________________________________________________________ Was denken Sie über E-Mail? Wir hören auf Ihre Meinung: http://surveylink.yahoo.com/wix/p0379378.aspx ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: Ant: Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-01 21:05 ` Ant: " Martin Chabr @ 2005-10-03 0:41 ` skaller 2005-10-03 1:13 ` Seth J. Fogarty 2005-10-03 13:09 ` Thomas Fischbacher 0 siblings, 2 replies; 44+ messages in thread From: skaller @ 2005-10-03 0:41 UTC (permalink / raw) To: Martin Chabr; +Cc: Pal-Kristian Engstad, caml-list On Sat, 2005-10-01 at 23:05 +0200, Martin Chabr wrote: > Hello Pal-Kristian, > > I agree with you that functional code written in a > tail recursive style is hard to read. Sometimes you > have to do it that way if you want to avoid a stack > overflow. > > I hope that one day functional language compilers will > do that optimization for you - convert a > non-tail-recursive code into a tail-recursive one. Do > you know of some progress in that direction? Isn't that just CPS? -- John Skaller <skaller at users dot sf dot net> Felix, successor to C++: http://felix.sf.net ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: Ant: Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-03 0:41 ` skaller @ 2005-10-03 1:13 ` Seth J. Fogarty 2005-10-03 13:09 ` Thomas Fischbacher 1 sibling, 0 replies; 44+ messages in thread From: Seth J. Fogarty @ 2005-10-03 1:13 UTC (permalink / raw) To: caml-list On 10/2/05, skaller <skaller@users.sourceforge.net> wrote: > On Sat, 2005-10-01 at 23:05 +0200, Martin Chabr wrote: > > Hello Pal-Kristian, > > > > I agree with you that functional code written in a > > tail recursive style is hard to read. Sometimes you > > have to do it that way if you want to avoid a stack > > overflow. > > > > I hope that one day functional language compilers will > > do that optimization for you - convert a > > non-tail-recursive code into a tail-recursive one. Do > > you know of some progress in that direction? > > Isn't that just CPS? -- Seth Fogarty sfogarty@[gmail.com|rice.edu|livejournal] Neep-neep at large AIM: Sorrath "I know there are people in this world who do not love their fellow human beings - and I hate people like that" --Tom Lehrer. ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: Ant: Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-03 0:41 ` skaller 2005-10-03 1:13 ` Seth J. Fogarty @ 2005-10-03 13:09 ` Thomas Fischbacher 2005-10-03 14:57 ` skaller ` (2 more replies) 1 sibling, 3 replies; 44+ messages in thread From: Thomas Fischbacher @ 2005-10-03 13:09 UTC (permalink / raw) To: skaller; +Cc: Martin Chabr, Pal-Kristian Engstad, caml-list On Mon, 3 Oct 2005, skaller wrote: > > I hope that one day functional language compilers will > > do that optimization for you - convert a > > non-tail-recursive code into a tail-recursive one. Do > > you know of some progress in that direction? > > Isn't that just CPS? He presumably wanted to see a different thing, e.g. automatically transforming let rec fac1 x = if x = 0 then 1 else x*(fac1 (x-1)) ;; into let fac2 x = let rec walk so_far todo = if todo = 0 then so_far else walk (todo*so_far) (todo-1) in walk 1 x ;; My impression is that it may indeed be possible to do something like this for simple applications, but that the cases that can be covered automatically presumably are so limited that an experienced programmer will usually want to attack them by going to a higher form of abstraction and not waste time on such things anyway. I think that Olin Shivers indeed does have a valid point in pointing out that writing loops in tail-recursive style has major disadvantages. However, my impression still is that as soon as someone thinks in terms of "I have to write a loop for this", chances are good that he may improve his design by going back one step and ask the question "what do I want to use that loop for?". In quite many situations, it is possible to express one's thoughts more directly via other means, such as Array.map, fold_left, etc. What I (as a pedestrian) especially do not like about loops is that it is much easier to make off-by-one errors than with any form of recursion which contains a base-case/recursive-case analysis. Unfortunately, the OCaml native code compiler apparently is not yet smart enough to optimize code written in such a higher-order style well enough so that it can compete with imperative or tail-recursive code in time-critical applications. (Though quite many applications in fact are not.) At present, one can expect to lose about a factor of ~3 in performance. Example: ===> let timing_apply f x = let t0 = Unix.gettimeofday() in let f_x = f x in let t1 = Unix.gettimeofday() in (f_x,t1-.t0) ;; let my_array_fold_left f init arr = let len = Array.length arr in let rec walk so_far pos = if pos=len then so_far else walk (f so_far arr.(pos)) (1+pos) in walk init 0 ;; let test m n = let arr = Array.init m (fun j -> Array.init n (fun k -> j*k+k)) in let scratchpad = ref 0 in (* -- *) let rec frobenius1 mx = Array.fold_left (fun so_far row -> Array.fold_left (fun so_far entry -> so_far+entry*entry) so_far row) 0 mx in let frobenius2 mx = my_array_fold_left (fun so_far row -> my_array_fold_left (fun so_far entry -> so_far+entry*entry) so_far row) 0 mx in let frobenius3 mx = begin scratchpad := 0; for i=0 to (Array.length mx)-1 do let row = mx.(i) in for j=0 to (Array.length row)-1 do scratchpad:= !scratchpad + row.(j)*row.(j); done; done; !scratchpad end in let frobenius4 mx = let nr_rows = Array.length mx in let rec walk_rows so_far nr_row = if nr_row = nr_rows then so_far else let row = mx.(nr_row) in let len_row = Array.length row in let rec walk_cols so_far nr_col = if nr_col = len_row then so_far else walk_cols (so_far+row.(nr_col)*row.(nr_col)) (1+nr_col) in walk_rows (walk_cols so_far 0) (1+nr_row) in walk_rows 0 0 in let frobenius5 mx = let nr_rows = Array.length mx in let rec walk_rows so_far nr_row = if nr_row = nr_rows then so_far else let row = mx.(nr_row) in let len_row = Array.length row in let rec walk_cols row so_far nr_col = if nr_col = len_row then so_far else walk_cols row (so_far+row.(nr_col)*row.(nr_col)) (1+nr_col) in walk_rows (walk_cols row so_far 0) (1+nr_row) in walk_rows 0 0 in let compute_n_times n f x = let rec walk k = if k = n then f x else let () = ignore(f x) in walk (k+1) in walk 1 in Array.map (fun f -> timing_apply (compute_n_times 1000 f) arr) [|frobenius1;frobenius2;frobenius3;frobenius4;frobenius5|] ;; let result = test 1000 3 in Array.iteri (fun nr (_,t) -> Printf.printf "%d: %f\n" (1+nr) t) result ;; <=== ocamlc: 1: 0.987257 2: 1.196910 3: 0.709074 4: 0.858948 5: 0.984935 ocamlopt: 1: 0.066404 2: 0.075691 3: 0.025450 4: 0.025756 5: 0.023472 -- regards, tf@cip.physik.uni-muenchen.de (o_ Thomas Fischbacher - http://www.cip.physik.uni-muenchen.de/~tf //\ (lambda (n) ((lambda (p q r) (p p q r)) (lambda (g x y) V_/_ (if (= x 0) y (g g (- x 1) (* x y)))) n 1)) (Debian GNU) ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: Ant: Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-03 13:09 ` Thomas Fischbacher @ 2005-10-03 14:57 ` skaller 2005-10-03 20:03 ` Ant: " Martin Chabr 2005-10-05 11:14 ` Andrej Bauer 2 siblings, 0 replies; 44+ messages in thread From: skaller @ 2005-10-03 14:57 UTC (permalink / raw) To: Thomas Fischbacher; +Cc: Martin Chabr, Pal-Kristian Engstad, caml-list On Mon, 2005-10-03 at 15:09 +0200, Thomas Fischbacher wrote: > On Mon, 3 Oct 2005, skaller wrote: > > > > I hope that one day functional language compilers will > > > do that optimization for you - convert a > > > non-tail-recursive code into a tail-recursive one. Do > > > you know of some progress in that direction? > > > > Isn't that just CPS? > > He presumably wanted to see a different thing, e.g. > automatically transforming I know. But his comment 'that's different' is inadequate. CPS transforms code so there are no function calls at all, (procedural form), or, in functional form, every function contains exactly one call which is always in tail position. So it certainly does what is required. Therefore the question is nonsense. The real question is more like asking if it is possible to transform a routine using O(n^k) store into one using O(n^j) store, where j < k. It is always possible to eliminate recursion: for example, a recursive descent of a simple tree can always be replaced by a using an iterator .. of course the iterator will have to retain a list of parent nodes to allow the traversal, so eliminating the 'recursion' does not save any storage. -- John Skaller <skaller at users dot sf dot net> Felix, successor to C++: http://felix.sf.net ^ permalink raw reply [flat|nested] 44+ messages in thread
* Ant: Re: Ant: Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-03 13:09 ` Thomas Fischbacher 2005-10-03 14:57 ` skaller @ 2005-10-03 20:03 ` Martin Chabr 2005-10-03 20:25 ` Thomas Fischbacher ` (2 more replies) 2005-10-05 11:14 ` Andrej Bauer 2 siblings, 3 replies; 44+ messages in thread From: Martin Chabr @ 2005-10-03 20:03 UTC (permalink / raw) To: Thomas Fischbacher; +Cc: caml-list Thomas, what I am especially concerned about is the stack overflow for non-tail-recursive programs. Speed is of less importance. By the way, we are diverting from the subject of this thread and from the main cause of my concern: Avoiding shared data. Why is it done that way? Who wants to have an array or list of identical records or sub-arrays or other sub-structures which are all updated at the same time in the same way? In other words, who wants to have an array or a list of pointers which are pointing to the same thing? Does it ever happen in OCaml if programming is done in a purely functional way? Does it happen in Haskell? Martin --- Thomas Fischbacher <Thomas.Fischbacher@Physik.Uni-Muenchen.DE> schrieb: > > On Mon, 3 Oct 2005, skaller wrote: > > > > I hope that one day functional language > compilers will > > > do that optimization for you - convert a > > > non-tail-recursive code into a tail-recursive > one. Do > > > you know of some progress in that direction? > > > > Isn't that just CPS? > > He presumably wanted to see a different thing, e.g. > automatically transforming > > > let rec fac1 x = > if x = 0 > then 1 > else x*(fac1 (x-1)) > ;; > > into > > let fac2 x = > let rec walk so_far todo = > if todo = 0 > then so_far > else walk (todo*so_far) (todo-1) > in walk 1 x > ;; > > > My impression is that it may indeed be possible to > do something like this > for simple applications, but that the cases that can > be covered > automatically presumably are so limited that an > experienced programmer > will usually want to attack them by going to a > higher form of abstraction > and not waste time on such things anyway. > > I think that Olin Shivers indeed does have a valid > point in pointing out > that writing loops in tail-recursive style has major > disadvantages. > However, my impression still is that as soon as > someone thinks in terms of > "I have to write a loop for this", chances are good > that he may improve > his design by going back one step and ask the > question "what do I want to > use that loop for?". In quite many situations, it is > possible to express > one's thoughts more directly via other means, such > as Array.map, > fold_left, etc. > > What I (as a pedestrian) especially do not like > about loops is that it is > much easier to make off-by-one errors than with any > form of recursion > which contains a base-case/recursive-case analysis. > > > Unfortunately, the OCaml native code compiler > apparently is not yet smart > enough to optimize code written in such a > higher-order style well enough > so that it can compete with imperative or > tail-recursive code in > time-critical applications. (Though quite many > applications in fact are > not.) At present, one can expect to lose about a > factor of ~3 in > performance. > > Example: > > ===> > let timing_apply f x = > let t0 = Unix.gettimeofday() in > let f_x = f x in > let t1 = Unix.gettimeofday() in > (f_x,t1-.t0) > ;; > > let my_array_fold_left f init arr = > let len = Array.length arr in > let rec walk so_far pos = > if pos=len > then so_far > else walk (f so_far arr.(pos)) (1+pos) > in walk init 0 > ;; > > > let test m n = > let arr = > Array.init m > (fun j -> Array.init n (fun k -> j*k+k)) > in > let scratchpad = ref 0 in > (* -- *) > let rec frobenius1 mx = > Array.fold_left > (fun so_far row -> > Array.fold_left > (fun so_far entry -> > so_far+entry*entry) > so_far row) > 0 mx > in > let frobenius2 mx = > my_array_fold_left > (fun so_far row -> > my_array_fold_left > (fun so_far entry -> > so_far+entry*entry) > so_far row) > 0 mx > in > let frobenius3 mx = > begin > scratchpad := 0; > for i=0 to (Array.length mx)-1 do > let row = mx.(i) in > for j=0 to (Array.length row)-1 do > scratchpad:= !scratchpad + row.(j)*row.(j); > done; > done; > !scratchpad > end > in > let frobenius4 mx = > let nr_rows = Array.length mx in > let rec walk_rows so_far nr_row = > if nr_row = nr_rows > then so_far > else > let row = mx.(nr_row) in > let len_row = Array.length row in > let rec walk_cols so_far nr_col = > if nr_col = len_row > then so_far > else walk_cols (so_far+row.(nr_col)*row.(nr_col)) > (1+nr_col) > in > walk_rows (walk_cols so_far 0) (1+nr_row) > in > walk_rows 0 0 > in > let frobenius5 mx = > let nr_rows = Array.length mx in > let rec walk_rows so_far nr_row = > if nr_row = nr_rows > then so_far > else > let row = mx.(nr_row) in > let len_row = Array.length row in > let rec walk_cols row so_far nr_col = > if nr_col = len_row > then so_far > else walk_cols row > (so_far+row.(nr_col)*row.(nr_col)) (1+nr_col) > in > walk_rows (walk_cols row so_far 0) (1+nr_row) > in > walk_rows 0 0 > in > let compute_n_times n f x = > let rec walk k = > if k = n then f x > else > let () = ignore(f x) in > walk (k+1) > in walk 1 > in > Array.map > (fun f -> timing_apply (compute_n_times 1000 f) > arr) > > [|frobenius1;frobenius2;frobenius3;frobenius4;frobenius5|] > ;; > > let result = test 1000 3 in > Array.iteri (fun nr (_,t) -> Printf.printf "%d: > %f\n" (1+nr) t) result > ;; > <=== > > ocamlc: > > 1: 0.987257 > 2: 1.196910 > 3: 0.709074 > 4: 0.858948 > 5: 0.984935 > > ocamlopt: > === message truncated === ___________________________________________________________ Gesendet von Yahoo! Mail - Jetzt mit 1GB Speicher kostenlos - Hier anmelden: http://mail.yahoo.de ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: Ant: Re: Ant: Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-03 20:03 ` Ant: " Martin Chabr @ 2005-10-03 20:25 ` Thomas Fischbacher 2005-10-03 21:08 ` Jon Harrop 2005-10-04 2:53 ` skaller 2 siblings, 0 replies; 44+ messages in thread From: Thomas Fischbacher @ 2005-10-03 20:25 UTC (permalink / raw) To: Martin Chabr; +Cc: caml-list On Mon, 3 Oct 2005, Martin Chabr wrote: > Avoiding shared data. Why is it done that way? Who > wants to have an array or list of identical records or > sub-arrays or other sub-structures which are all > updated at the same time in the same way? Thoughts about substructure updates and sharing structure do not mix. There are situations where the one way to think about things is appropriate, and there are situations when it's the other one. > In other > words, who wants to have an array or a list of > pointers which are pointing to the same thing? Suppose I give you a tree representing the filesystem structure on my machine. Now I ask you to map this to the tree describing the filesystem with one directory renamed and another one (and everythinbg beneath it) deleted. You surely do not want to copy the entire tree, so this is an example where sharing structure perfectly makes sense. And as you do not destructively modify the data in the nodes, it does not matter at all for the semantics of the program that you actually share a lot of data. -- regards, tf@cip.physik.uni-muenchen.de (o_ Thomas Fischbacher - http://www.cip.physik.uni-muenchen.de/~tf //\ (lambda (n) ((lambda (p q r) (p p q r)) (lambda (g x y) V_/_ (if (= x 0) y (g g (- x 1) (* x y)))) n 1)) (Debian GNU) ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: Ant: Re: Ant: Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-03 20:03 ` Ant: " Martin Chabr 2005-10-03 20:25 ` Thomas Fischbacher @ 2005-10-03 21:08 ` Jon Harrop 2005-10-04 18:06 ` Ant: " Martin Chabr 2005-10-04 2:53 ` skaller 2 siblings, 1 reply; 44+ messages in thread From: Jon Harrop @ 2005-10-03 21:08 UTC (permalink / raw) To: caml-list On Monday 03 October 2005 21:03, Martin Chabr wrote: > what I am especially concerned about is the stack > overflow for non-tail-recursive programs. Speed is of > less importance. Yes. Many useful algorithms are non-tail recursive and do not cause stack overflows, e.g. those that recurse O(log n) times. > By the way, we are diverting from the subject of this > thread and from the main cause of my concern: > > Avoiding shared data. Why is it done that way? Who > wants to have an array or list of identical records or > sub-arrays or other sub-structures which are all > updated at the same time in the same way? In other > words, who wants to have an array or a list of > pointers which are pointing to the same thing? If you mean this: # let a = Array.make 3 (ref 0) in (a.(0) := 3; a);; - : int ref array = [|{contents = 3}; {contents = 3}; {contents = 3}|] I don't want arrays of elements referencing the same value. So I don't create them. > Does it ever happen in OCaml if programming is done in > a purely functional way? If you mean "is referential transparency useful?" then the answer is definitely yes. I use data structures that share data all the time but not in the form of an array or list of the same repeated value. For example, computing set unions, differences and intersections using the Set module reuses branches of old tree when possible. This reduces the asymptotic algorithmic complexity of these operations so they can run asymptotically faster than imperative implementations. > Does it happen in Haskell? You'll get a suboptimal answer to such questions on this list. -- Dr Jon D Harrop, Flying Frog Consultancy Ltd. Objective CAML for Scientists http://www.ffconsultancy.com/products/ocaml_for_scientists ^ permalink raw reply [flat|nested] 44+ messages in thread
* Ant: Re: Ant: Re: Ant: Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-03 21:08 ` Jon Harrop @ 2005-10-04 18:06 ` Martin Chabr 2005-10-04 18:32 ` Jon Harrop 0 siblings, 1 reply; 44+ messages in thread From: Martin Chabr @ 2005-10-04 18:06 UTC (permalink / raw) To: Jon Harrop; +Cc: caml-list --- Jon Harrop <jon@ffconsultancy.com> wrote: > If you mean this: > > # let a = Array.make 3 (ref 0) in (a.(0) := 3; a);; > - : int ref array = [|{contents = 3}; {contents = > 3}; {contents = 3}|] > > I don't want arrays of elements referencing the same > value. So I don't create > them. I do not disagree. The trouble on my side is that it is very easy to get them and difficult not to get them or get rid of them, once the programs get more complex. Martin ___________________________________________________________ Was denken Sie über E-Mail? Wir hören auf Ihre Meinung: http://surveylink.yahoo.com/wix/p0379378.aspx ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: Ant: Re: Ant: Re: Ant: Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-04 18:06 ` Ant: " Martin Chabr @ 2005-10-04 18:32 ` Jon Harrop 0 siblings, 0 replies; 44+ messages in thread From: Jon Harrop @ 2005-10-04 18:32 UTC (permalink / raw) To: caml-list On Tuesday 04 October 2005 19:06, you wrote: > I do not disagree. The trouble on my side is that it > is very easy to get them and difficult not to get them > or get rid of them, once the programs get more > complex. Yes, misuse of Array.make is certainly a caveat. The easiest solution is to use Array.init instead. -- Dr Jon D Harrop, Flying Frog Consultancy Ltd. Objective CAML for Scientists http://www.ffconsultancy.com/products/ocaml_for_scientists ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: Ant: Re: Ant: Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-03 20:03 ` Ant: " Martin Chabr 2005-10-03 20:25 ` Thomas Fischbacher 2005-10-03 21:08 ` Jon Harrop @ 2005-10-04 2:53 ` skaller 2005-10-04 16:15 ` Brian Hurt 2005-10-04 18:09 ` Ant: Re: Ant: Re: Ant: Re: Ant: Re: [Caml-list] Avoiding shared data Martin Chabr 2 siblings, 2 replies; 44+ messages in thread From: skaller @ 2005-10-04 2:53 UTC (permalink / raw) To: Martin Chabr; +Cc: Thomas Fischbacher, caml-list On Mon, 2005-10-03 at 22:03 +0200, Martin Chabr wrote: > Avoiding shared data. Why is it done that way? Who > wants to have an array or list of identical records or > sub-arrays or other sub-structures which are all > updated at the same time in the same way? Mutable shared data is very useful: caching is an obvious example where this is precisely what you want. -- John Skaller <skaller at users dot sf dot net> Felix, successor to C++: http://felix.sf.net ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] Avoiding shared data 2005-10-04 2:53 ` skaller @ 2005-10-04 16:15 ` Brian Hurt 2005-10-04 16:47 ` FP/IP and performance (in general) and Patterns... (Re: [Caml-list] Avoiding shared data) Oliver Bandel 2005-10-04 18:09 ` Ant: Re: Ant: Re: Ant: Re: Ant: Re: [Caml-list] Avoiding shared data Martin Chabr 1 sibling, 1 reply; 44+ messages in thread From: Brian Hurt @ 2005-10-04 16:15 UTC (permalink / raw) To: skaller; +Cc: Martin Chabr, Thomas Fischbacher, caml-list On Tue, 4 Oct 2005, skaller wrote: > Mutable shared data is very useful: caching is > an obvious example where this is precisely what you want. Also, occassionally mutable data allows you to do something in O(1) instead of O(log N). If this extra cost is in your inner loop, it can slow the whole algorithm down by a factor of 10-30 fold easy. Graph algorithms are especially bad for this. Want to get a name for yourself? Come up with a way to represent cyclic graphs in a purely functional way such that given any arbtirary node, I can find out what other nodes it has edges to (and what their weights are) in O(1). Brian ^ permalink raw reply [flat|nested] 44+ messages in thread
* FP/IP and performance (in general) and Patterns... (Re: [Caml-list] Avoiding shared data) 2005-10-04 16:15 ` Brian Hurt @ 2005-10-04 16:47 ` Oliver Bandel 2005-10-04 22:38 ` Michael Wohlwend ` (2 more replies) 0 siblings, 3 replies; 44+ messages in thread From: Oliver Bandel @ 2005-10-04 16:47 UTC (permalink / raw) To: caml-list Hello, is there a general overvie (paper or book), where functional and imperative approaches to solve a problem are compared directly? Most often people say, imperative is faster in most of all cases, and only in some cases FP might be faster. If there is a paper/book about problem solving and performance, comparing different approaches I would be happy to have a pointer to it. Also it would be nice to find something about how classical imperative style, OO-style and FP-style are solving problems, and which patterns can be done easier in the one or the other way. I recently had a discussion about some OO-patterns ( so called "GoF"-Book) and tried to solve some of them with OCamls module system. Adapter and bridge was talked about. Adapter was easy in writing a simple wrapper. Bridge could be done with a functor. :) So, if there are direct translations possible, where to find comparisons? Another interesting theme in this respect is: If not only some patterns were adapted to Fp, but the whole problem solving approach is different, which patterns will be unnevcessary then, because the whole structure of the software will be different...?! Ciao, Oliver ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: FP/IP and performance (in general) and Patterns... (Re: [Caml-list] Avoiding shared data) 2005-10-04 16:47 ` FP/IP and performance (in general) and Patterns... (Re: [Caml-list] Avoiding shared data) Oliver Bandel @ 2005-10-04 22:38 ` Michael Wohlwend 2005-10-05 0:31 ` Jon Harrop 2005-10-04 22:39 ` Christopher A. Watford 2005-10-05 0:45 ` Brian Hurt 2 siblings, 1 reply; 44+ messages in thread From: Michael Wohlwend @ 2005-10-04 22:38 UTC (permalink / raw) To: caml-list On Tuesday 04 October 2005 18:47, Oliver Bandel wrote: > > So, if there are direct translations possible, where to find > comparisons? > there is a paper "Design Patterns in OCaml" which describes various patterns: http://people.csail.mit.edu/dnj/teaching/6898/projects/vicente-wagner.pdf cheers, Michael ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: FP/IP and performance (in general) and Patterns... (Re: [Caml-list] Avoiding shared data) 2005-10-04 22:38 ` Michael Wohlwend @ 2005-10-05 0:31 ` Jon Harrop 0 siblings, 0 replies; 44+ messages in thread From: Jon Harrop @ 2005-10-05 0:31 UTC (permalink / raw) To: caml-list On Tuesday 04 October 2005 23:38, Michael Wohlwend wrote: > there is a paper "Design Patterns in OCaml" which describes various > patterns: > http://people.csail.mit.edu/dnj/teaching/6898/projects/vicente-wagner.pdf I think I read this paper some time ago but it was interesting to read it again. Firstly, it gives only 3 references, doesn't appear to be complete, I think it is out of date and it appears to be written from the point of view of a dynamic typer. Secondly, I was never a fan of "design patterns". As they say, several "design patterns" have trivial counterparts in FPLs, roughly: Singleton = module structure, Facade = module signature, Command = currying, Memento = dynamic type checking, and Strategy = HOFs. The paper repeatedly states that modules cannot be mutually recursive. AFAIK, this has not been true for some time. In the section about the "Observer" pattern, they state "a meaningless method call must be inserted into the code because OCaml's static checker cannot infer the type of the ...". Can the meaningless method not be replaced with a type declaration? Perhaps the "State" pattern can be better represented by reusing polymorphic variants. Overall, I'd say that the idea of design patterns is reasonable but the GOFs work is not relevant to OCaml - the study will need to be redone from scratch. I am much more interested in the automation of the factoring of code patterns, although I've yet to actually study it... -- Dr Jon D Harrop, Flying Frog Consultancy Ltd. Objective CAML for Scientists http://www.ffconsultancy.com/products/ocaml_for_scientists ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: FP/IP and performance (in general) and Patterns... (Re: [Caml-list] Avoiding shared data) 2005-10-04 16:47 ` FP/IP and performance (in general) and Patterns... (Re: [Caml-list] Avoiding shared data) Oliver Bandel 2005-10-04 22:38 ` Michael Wohlwend @ 2005-10-04 22:39 ` Christopher A. Watford 2005-10-04 23:14 ` Jon Harrop 2005-10-05 12:10 ` Oliver Bandel 2005-10-05 0:45 ` Brian Hurt 2 siblings, 2 replies; 44+ messages in thread From: Christopher A. Watford @ 2005-10-04 22:39 UTC (permalink / raw) To: Oliver Bandel; +Cc: caml-list On 10/4/05, Oliver Bandel <oliver@first.in-berlin.de> wrote: > Hello, > > is there a general overvie (paper or book), where > functional and imperative approaches to solve a problem are > compared directly? > > Most often people say, imperative is faster in most of all > cases, and only in some cases FP might be faster. > > If there is a paper/book about problem solving and performance, > comparing different approaches I would be happy to have a pointer > to it. > > Also it would be nice to find something about how > classical imperative style, OO-style and FP-style > are solving problems, and which patterns can be done > easier in the one or the other way. > > I recently had a discussion about some OO-patterns > ( so called "GoF"-Book) and tried to solve some of them with > OCamls module system. > Adapter and bridge was talked about. Adapter was easy in writing a simple > wrapper. > Bridge could be done with a functor. :) > > So, if there are direct translations possible, where to find > comparisons? > > Another interesting theme in this respect is: If not only some patterns > were adapted to Fp, but the whole problem solving approach is different, > which patterns will be unnevcessary then, because the whole structure of > the software will be different...?! > > Ciao, > Oliver Sigh, your question only begs for trolls to pop out of the corners. It would probably be best to keep these questions off this list. Far too often they go off into the deep end of the 'my language is better than your language' debates. Obviously members of this list are going to be biased towards functional languages and will probably give you biased articles to read or biased papers. Not necessarily biased in a bad way, just more likely to show functional languages in a good light. A poor place to start reading on the subject of what language is best is The Computer Language Shootout [1]. An even worse place to start reading is this list. The actual performance for any program will depend largely on your ability to program well for the language and for the specific compiler you choose. I would say it's obvious that some patterns are easier to *write* in a certain language, but that hardly shows which language is *best* speed wise. Your example shows one pattern functional languages are well suited for, however, I'm sure an experienced C programmer could show you an example of that pattern which is both efficient and easy to read (I know I know people on this list may disagree). This list is best for asking OCaml questions and is awful for asking for what language is best for what, nobody agrees. -- Christopher A. Watford christopher.watford@gmail.com [1] http://shootout.alioth.debian.org/ ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: FP/IP and performance (in general) and Patterns... (Re: [Caml-list] Avoiding shared data) 2005-10-04 22:39 ` Christopher A. Watford @ 2005-10-04 23:14 ` Jon Harrop 2005-10-05 12:10 ` Oliver Bandel 1 sibling, 0 replies; 44+ messages in thread From: Jon Harrop @ 2005-10-04 23:14 UTC (permalink / raw) To: caml-list On Tuesday 04 October 2005 23:39, Christopher A. Watford wrote: > This list is best for asking OCaml questions and is awful for asking > for what language is best for what, nobody agrees. Oliver was asking which of three programming paradigms is best under different circumstances. OCaml provides all three so I think this can be taken as an OCaml-specific question and is fine for this list. I for one would be very interested in reading information on this. I'll check out the design patterns paper - thanks Michael. My impression is that: 1. Data structure heavy code is often easier to write in a functional style. See the "n"th-nearest neighbour example from my book: http://www.ffconsultancy.com/products/ocaml_for_scientists/complete/ 2. GUI code is more easily written with a stateful imperative front and often a functional back end. I have yet to really exploit OCaml's OO capabilities. -- Dr Jon D Harrop, Flying Frog Consultancy Ltd. Objective CAML for Scientists http://www.ffconsultancy.com/products/ocaml_for_scientists ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: FP/IP and performance (in general) and Patterns... (Re: [Caml-list] Avoiding shared data) 2005-10-04 22:39 ` Christopher A. Watford 2005-10-04 23:14 ` Jon Harrop @ 2005-10-05 12:10 ` Oliver Bandel 2005-10-05 13:08 ` Jon Harrop ` (2 more replies) 1 sibling, 3 replies; 44+ messages in thread From: Oliver Bandel @ 2005-10-05 12:10 UTC (permalink / raw) To: caml-list On Tue, Oct 04, 2005 at 06:39:28PM -0400, Christopher A. Watford wrote: [...] > This list is best for asking OCaml questions and is awful for asking > for what language is best for what, nobody agrees. [...] I was not asking, what language is better for soimething, or best at all. I asked for what programming style/paradigm is best used to solve certain problems. As OCaml provides more than one programming paradigm, for me it's the best language I ever had programmed in (some other features are also fine). So the question goes in a different direction: How to solve problems best, if you have the possibility do do it in different ways. Well, every programmer can choose it's own way, but if certain areas are well known to do them in a certain way best, it makes sense to go that direction. For example, I'm not really a fan of OO-programming, but when programming GUI-software I think it would be the best choice, and FP-programming lacks flexibility here (if not using a DSL to create GUI-code, which then is separately compiled). Ciao, Oliver ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: FP/IP and performance (in general) and Patterns... (Re: [Caml-list] Avoiding shared data) 2005-10-05 12:10 ` Oliver Bandel @ 2005-10-05 13:08 ` Jon Harrop 2005-10-05 15:28 ` skaller 2005-10-05 20:52 ` Ant: " Martin Chabr 2 siblings, 0 replies; 44+ messages in thread From: Jon Harrop @ 2005-10-05 13:08 UTC (permalink / raw) To: caml-list On Wednesday 05 October 2005 13:10, Oliver Bandel wrote: > For example, I'm not really a fan of OO-programming, but when > programming GUI-software I think it would be the best choice, > and FP-programming lacks flexibility here (if not using > a DSL to create GUI-code, which then is separately compiled). I am finding ordinary FP perfectly adequate for GUI programming. My GUI programming may be unorthodox, however. :-) -- Dr Jon D Harrop, Flying Frog Consultancy Ltd. Objective CAML for Scientists http://www.ffconsultancy.com/products/ocaml_for_scientists ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: FP/IP and performance (in general) and Patterns... (Re: [Caml-list] Avoiding shared data) 2005-10-05 12:10 ` Oliver Bandel 2005-10-05 13:08 ` Jon Harrop @ 2005-10-05 15:28 ` skaller 2005-10-05 20:52 ` Ant: " Martin Chabr 2 siblings, 0 replies; 44+ messages in thread From: skaller @ 2005-10-05 15:28 UTC (permalink / raw) To: Oliver Bandel; +Cc: caml-list On Wed, 2005-10-05 at 14:10 +0200, Oliver Bandel wrote: > For example, I'm not really a fan of OO-programming, but when > programming GUI-software I think it would be the best choice, > and FP-programming lacks flexibility here I contend that concurrent programming is best here. However, I don't know: I have a tool (Felix) to explore this, but haven't got around to actually trying the one-thread/one-widget paradigm. The OO solution is reactive and requires you to maintain the whole widget state without use of the stack, thereby discarding everything that is known about block structured and functional programming, and going back to the bad old days of the early 70s with a flat state space, except that the state space is partitioned into objects. The thread based solution control inverts this paradigm, giving you a stack and control flow for every widget, this is *active* or algorithmic programming. So I content it must be better, because it not only partitions the state space as OO does, it *also* partitions the control space, and allows you to use all the advantages of a functional or block structured style. Actually, in practice, I expect a mix of the solutions will be best. The reason is: we know a LOT about state machines. So subparts of problems which correspond to them can easily be programmed and reasoned about, simply because we have lots of mature theory. -- John Skaller <skaller at users dot sf dot net> Felix, successor to C++: http://felix.sf.net ^ permalink raw reply [flat|nested] 44+ messages in thread
* Ant: Re: FP/IP and performance (in general) and Patterns... (Re: [Caml-list] Avoiding shared data) 2005-10-05 12:10 ` Oliver Bandel 2005-10-05 13:08 ` Jon Harrop 2005-10-05 15:28 ` skaller @ 2005-10-05 20:52 ` Martin Chabr 2005-10-05 23:21 ` Markus Mottl 2 siblings, 1 reply; 44+ messages in thread From: Martin Chabr @ 2005-10-05 20:52 UTC (permalink / raw) To: Oliver Bandel; +Cc: caml-list >From my limited experience I see it this way: Mathematical computations, algorithms, interfacing (complicated data conversions), text translations, parsing, anything which can be well represented by dataflow diagrams and where there is no changing state ==> use FP User interfaces, business systems, anything with objects which have changing states and which react to events and interact with each other ==> use OOP Martin --- Oliver Bandel <oliver@first.in-berlin.de> wrote: > On Tue, Oct 04, 2005 at 06:39:28PM -0400, > Christopher A. Watford wrote: > [...] > > This list is best for asking OCaml questions and > is awful for asking > > for what language is best for what, nobody agrees. > [...] > > > I was not asking, what language is better for > soimething, or best at all. > I asked for what programming style/paradigm is best > used to solve > certain problems. > > As OCaml provides more than one programming > paradigm, for me > it's the best language I ever had programmed in > (some > other features are also fine). > > So the question goes in a different direction: > How to solve problems best, if you have the > possibility > do do it in different ways. > > Well, every programmer can choose it's own way, but > if certain areas > are well known to do them in a certain way best, it > makes sense > to go that direction. > > For example, I'm not really a fan of OO-programming, > but when > programming GUI-software I think it would be the > best choice, > and FP-programming lacks flexibility here (if not > using > a DSL to create GUI-code, which then is separately > compiled). > > Ciao, > Oliver > > _______________________________________________ > Caml-list mailing list. Subscription management: > http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list > Archives: http://caml.inria.fr > Beginner's list: > http://groups.yahoo.com/group/ocaml_beginners > Bug reports: http://caml.inria.fr/bin/caml-bugs > ___________________________________________________________ Gesendet von Yahoo! Mail - Jetzt mit 1GB Speicher kostenlos - Hier anmelden: http://mail.yahoo.de ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: Ant: Re: FP/IP and performance (in general) and Patterns... (Re: [Caml-list] Avoiding shared data) 2005-10-05 20:52 ` Ant: " Martin Chabr @ 2005-10-05 23:21 ` Markus Mottl 2005-10-06 16:54 ` brogoff 0 siblings, 1 reply; 44+ messages in thread From: Markus Mottl @ 2005-10-05 23:21 UTC (permalink / raw) To: Martin Chabr; +Cc: Oliver Bandel, caml-list On 10/5/05, Martin Chabr <martin_chabr@yahoo.de> wrote: > User interfaces, business systems, anything with > objects which have changing states and which react to > events and interact with each other ==> use OOP FWIW, we use OCaml for fairly large systems (> 100 KLOCs, > 1000 modules) with very complicated business logic handling high-volume realtime events. Even though OCaml supports OOP very well, probably much better than most mainstream languages, we do not use OOP and are not intending to do so. Mutable records together with modules are perfectly fine for handling changing states safely and efficiently and are in the general case semantically more transparent than objects. Your mileage may vary... Regards, Markus -- Markus Mottl http://www.ocaml.info markus.mottl@gmail.com ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: Ant: Re: FP/IP and performance (in general) and Patterns... (Re: [Caml-list] Avoiding shared data) 2005-10-05 23:21 ` Markus Mottl @ 2005-10-06 16:54 ` brogoff 0 siblings, 0 replies; 44+ messages in thread From: brogoff @ 2005-10-06 16:54 UTC (permalink / raw) To: caml-list On Wed, 5 Oct 2005, Markus Mottl wrote: > FWIW, we use OCaml for fairly large systems (> 100 KLOCs, > 1000 > modules) with very complicated business logic handling high-volume > realtime events. Even though OCaml supports OOP very well, probably > much better than most mainstream languages, we do not use OOP and are > not intending to do so. Mutable records together with modules are > perfectly fine for handling changing states safely and efficiently and > are in the general case semantically more transparent than objects. > Your mileage may vary... I don't disagree with anything above, but thought I'd mention that OCaml classes can be used as more polymorphic records (giving up pattern matching and some performance), without using the late-binding/open-recursion aspect which is the sine qua non of OOP. -- Brian ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: FP/IP and performance (in general) and Patterns... (Re: [Caml-list] Avoiding shared data) 2005-10-04 16:47 ` FP/IP and performance (in general) and Patterns... (Re: [Caml-list] Avoiding shared data) Oliver Bandel 2005-10-04 22:38 ` Michael Wohlwend 2005-10-04 22:39 ` Christopher A. Watford @ 2005-10-05 0:45 ` Brian Hurt 2 siblings, 0 replies; 44+ messages in thread From: Brian Hurt @ 2005-10-05 0:45 UTC (permalink / raw) To: Oliver Bandel; +Cc: caml-list On Tue, 4 Oct 2005, Oliver Bandel wrote: > Most often people say, imperative is faster in most of all > cases, and only in some cases FP might be faster. The general argument here is that if the FP solution is faster, the imperitive language can just implement the FP solution. My answer to that is that imperitive programming languages discourage you from thinking in certain ways rather strongly- while the FP solution may be faster, it's not "natural". The big advantage of FP programming IMHO is not performance, but instead *correctness*. With today's multi-gigahertz machines with multi-gigabytes of memory, performance isn't as critical as it used to be. But correctness- especially automatically gaurenteed correctness on projects spanning hundreds of thousands of lines of code and dozens of developers maintained over decades of time- starts becoming critical. I'd quite happily trade off 10% performance for correctness, or even 50% performance. FP is a huge gain in correctness, because it allows me to *control mutability*. If I pass a mutable data structure to a block of code there is an instant implicit contract between the caller and the callee on how (or wether) to modify the mutable data structure. It doesn't matter what the contract is- wether it's to not modify the structure at all, to allow optional modification (either unlimited or only in certain ways), or to require certain modifications- a dependency between the two different peices of code exists. And this dependency, this contract, is probably undocumented and always unchecked by the compiler, which means it's a maintaince nightmare waiting to happen. One peice of code gets modified to violate the contract, perhaps even unknowingly, or perhaps due to some changing requirement, and untouched code thousands of lines away suddenly breaks. Note that such a bidirectional dependency can be implemented in functional code- the called code returns a new copy of the modified structure to the calling code, which the calling code then adopts and uses. But notice that such a bidirectional dependency is explicitly stated in the code, and automatically checked by the compiler. And the calling code has the option of wether or not to allow the modification (or to use both the old and the new copy). Java programmers are begining to tumble to this advantage- notice the increasing popularity of immutable objects (starting with Int and Float), and Java Beans. > I recently had a discussion about some OO-patterns > ( so called "GoF"-Book) and tried to solve some of them with > OCamls module system. > Adapter and bridge was talked about. Adapter was easy in writing a simple > wrapper. > Bridge could be done with a functor. :) Some of the patterns are doable with modules. Singleton, for example, is a classic module pattern. Many of the patterns aren't, however. An important comment here is that modules are NOT weird and broken objects. A better point of comparison is C++ templates, except that 95+% of the use of templates in C++ is to implement universal types, i.e. a C++ programmer writes "list<A>" where an Ocaml programmer would write "'a list". > > Another interesting theme in this respect is: If not only some patterns > were adapted to Fp, but the whole problem solving approach is different, > which patterns will be unnevcessary then, because the whole structure of > the software will be different...?! There are patterns in every programming idiom. The GoF book was just Object Oriented patterns- but there are procedural patterns, and functional patterns, and logic patterns, etc. Brian ^ permalink raw reply [flat|nested] 44+ messages in thread
* Ant: Re: Ant: Re: Ant: Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-04 2:53 ` skaller 2005-10-04 16:15 ` Brian Hurt @ 2005-10-04 18:09 ` Martin Chabr 2005-10-05 8:42 ` skaller 1 sibling, 1 reply; 44+ messages in thread From: Martin Chabr @ 2005-10-04 18:09 UTC (permalink / raw) To: skaller; +Cc: caml-list --- skaller <skaller@users.sourceforge.net> wrote: > On Mon, 2005-10-03 at 22:03 +0200, Martin Chabr > wrote: > > > Avoiding shared data. Why is it done that way? Who > > wants to have an array or list of identical > records or > > sub-arrays or other sub-structures which are all > > updated at the same time in the same way? > > Mutable shared data is very useful: caching is > an obvious example where this is precisely what you > want. > > -- > John Skaller <skaller at users dot sf dot net> > Felix, successor to C++: http://felix.sf.net > > This sounds very interesting. Can you say a little more about it please? Martin ___________________________________________________________ Was denken Sie über E-Mail? Wir hören auf Ihre Meinung: http://surveylink.yahoo.com/wix/p0379378.aspx ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: Ant: Re: Ant: Re: Ant: Re: Ant: Re: [Caml-list] Avoiding shared data 2005-10-04 18:09 ` Ant: Re: Ant: Re: Ant: Re: Ant: Re: [Caml-list] Avoiding shared data Martin Chabr @ 2005-10-05 8:42 ` skaller 0 siblings, 0 replies; 44+ messages in thread From: skaller @ 2005-10-05 8:42 UTC (permalink / raw) To: Martin Chabr; +Cc: caml-list On Tue, 2005-10-04 at 20:09 +0200, Martin Chabr wrote: > > Mutable shared data is very useful: caching is > > an obvious example where this is precisely what you > > want. > This sounds very interesting. Can you say a little > more about it please? Well others know more about techniques like HashConsing and memoisation than I do .. but basically it is sometimes useful for a pure function which is expensive to evaluate, to have a cache of results already computed. For example, in Felix I have to do 'lookup' to bind string names of functions to entries in the symbol table. The lookup is very expensive for functions, because Felix supports overloading, which means the argument type has to be calculated, and unification against bound signatures of candidates is used to select the most specialised matching function. the problem is that to bind the types required to do this *also* requires lookup (and it can even be recursive). So in the process of doing all this lookup, it would be nice to cache the results so that the 'expensive' lookup is only done once, and after that the cached value is used. In fact I tried that using a Hashtbl .. but because the keys were complex, it turned out that polymorphic compare and hash functions were actually *slower* than my nasty purely functional lookup code. However, I do cache the environments, and this save some time: environments are created by a call like build_env (parent) which follows the chain of parents to ground, gathering all the symbols defined in each one (resulting in a list of hashtables). This process isn't all that slow .. the problem is that in a purely functional system with random symbol lookups, it has to be done once for EVERY lookup .. not just once when you do a lookup, since as noted above doing a lookup can spawn a hundreds of other lookups. So caching the 'environment' in which to do the lookups was a good way to speed up the process, without changing the functional style of the code. -- John Skaller <skaller at users dot sf dot net> Felix, successor to C++: http://felix.sf.net ^ permalink raw reply [flat|nested] 44+ messages in thread
* Re: [Caml-list] Avoiding shared data 2005-10-03 13:09 ` Thomas Fischbacher 2005-10-03 14:57 ` skaller 2005-10-03 20:03 ` Ant: " Martin Chabr @ 2005-10-05 11:14 ` Andrej Bauer 2 siblings, 0 replies; 44+ messages in thread From: Andrej Bauer @ 2005-10-05 11:14 UTC (permalink / raw) Cc: caml-list Thomas Fischbacher wrote: > He presumably wanted to see a different thing, e.g. > automatically transforming > let rec fac1 x = > if x = 0 > then 1 > else x*(fac1 (x-1)) > ;; > > into > > let fac2 x = > let rec walk so_far todo = > if todo = 0 > then so_far > else walk (todo*so_far) (todo-1) > in walk 1 x > ;; > > My impression is that it may indeed be possible to do something like this > for simple applications, but that the cases that can be covered > automatically presumably are so limited that an experienced programmer > will usually want to attack them by going to a higher form of abstraction > and not waste time on such things anyway. Incidentally, I recently wrote a little piece on how to transform one kind of recursion into another kind of recursion via propositions-as-types http://math.andrej.com/2005/09/16/proof-hacking/ which may be of interest to some of you. I only considered a very simple kind of recursion on natural numbers. My impression is that quite a general form of recursion could be treated (replace natural numbers by a well-founded type, for example). This sort of thing is probably not immediately useful in programming practice. But even "real" programmers should be aware of abstract mathematical ideas because they they indirectly make them write better programs. Best regards, Andrej ^ permalink raw reply [flat|nested] 44+ messages in thread
* Ant: Re: Ant: Re: [Caml-list] Avoiding shared data 2005-09-30 22:57 ` Oliver Bandel 2005-10-01 0:07 ` Pal-Kristian Engstad @ 2005-10-01 21:36 ` Martin Chabr 2005-10-03 11:51 ` getting used to FP-programming (Re: Ant: Re: Ant: Re: [Caml-list] Avoiding shared data) Oliver Bandel 1 sibling, 1 reply; 44+ messages in thread From: Martin Chabr @ 2005-10-01 21:36 UTC (permalink / raw) To: Oliver Bandel; +Cc: caml-list Hello Oliver, I am trying to find a programming style within the spectrum of possibilities which OCaml supports. This programming style should be easy to produce, easy to read and efficient in runtime. Sometimes a nested system of "for" or "while" loops appears simpler to me than a system of recursive calls. Sometimes such systems of recursive calls remind me of undisciplined goto jumps. There is an excellent OCaml tutorial at: http://www.ocaml-tutorial.org/. In this tutorial the author gives a simple example of a stack-blowing, non-tail-recursive code. The following tail-recursive version takes two functions instead of one and is relatively much more complex. In general, for the real world problems, it is much worse. I cite the author: "That was a brief overview of tail recursion, but in real world situations determining if a function is tail recursive can be quite hard." I believe him. This is at: http://www.ocaml-tutorial.org/if_statements,_loops_and_recursion section tail recursion. I think that some problems, like simple operations on lists, can be easier described by pattern matching and recursion, whereas for others it appears more natural to take loops. I also think that what looks simple or not depends on the person. I myself have spent half of my life with imperative languages. Regards, Martin --- Oliver Bandel <oliver@first.in-berlin.de> wrote: > On Mon, Sep 26, 2005 at 11:07:30PM +0200, Martin > Chabr wrote: > > Hello William, > > > > I am using a mutable record. I am programming this > 90% > > in the imperative (non-functional) style, so that > I > > can rewrite critical parts into Fortran easily. > > Another reason is, I am an intermediate user and > > finding out whether the recursion is a tail-one or > not > > is difficult for me. > > When you 90% of your code are writing in imperative > style > and do not go deeper into the functional/recursive > world, you will never be able to distinguish between > tail-rec and non-tail-rec style. > > But: It is not really hard to find the distinction > betwen > the two styles, but often the explanations are not > made > well. > Sometimes it's only one or two words in an > explanation about > tail-rec/non-tail-rec that must be substituted by > other words, > and the distinction can be made visible very easy. > > On the other hand: writing mor funtional/recursive > code will > make you more used to to this... > > Ciao, > Oliver > > _______________________________________________ > Caml-list mailing list. Subscription management: > http://yquem.inria.fr/cgi-bin/mailman/listinfo/caml-list > Archives: http://caml.inria.fr > Beginner's list: > http://groups.yahoo.com/group/ocaml_beginners > Bug reports: http://caml.inria.fr/bin/caml-bugs > ___________________________________________________________ Was denken Sie über E-Mail? Wir hören auf Ihre Meinung: http://surveylink.yahoo.com/wix/p0379378.aspx ^ permalink raw reply [flat|nested] 44+ messages in thread
* getting used to FP-programming (Re: Ant: Re: Ant: Re: [Caml-list] Avoiding shared data) 2005-10-01 21:36 ` Ant: Re: Ant: " Martin Chabr @ 2005-10-03 11:51 ` Oliver Bandel 0 siblings, 0 replies; 44+ messages in thread From: Oliver Bandel @ 2005-10-03 11:51 UTC (permalink / raw) To: caml-list Hello Martin, On Sat, Oct 01, 2005 at 11:36:08PM +0200, Martin Chabr wrote: > Hello Oliver, > > I am trying to find a programming style within the > spectrum of possibilities which OCaml supports. This > programming style should be easy to produce, easy to > read and efficient in runtime. > > Sometimes a nested system of "for" or "while" loops > appears simpler to me than a system of recursive > calls. Sometimes such systems of recursive calls > remind me of undisciplined goto jumps. [...] > general, for the real world problems, it is much > worse. I cite the author: > "That was a brief overview of tail recursion, but in > real world situations determining if a function is > tail recursive can be quite hard." I believe him. I doubt it is. Or maybe if the people write functions that are some pages of code long. But it's better to keep the functions short, and FP makes this very easy. [...] > I think that some problems, like simple operations on > lists, can be easier described by pattern matching and > recursion, whereas for others it appears more natural > to take loops. Yes, but when you are used to FP-style and recursions the amount of problems one would rather use the for/while loops for, gets smaller. I just dome days ago saw this on an example-code I wrote in a discussion on the berlin' Linux-User-Groups mailing list (I'm doing some marketing for OCaml;-)) I first thought, well the better version is with loops. But I tried the recursive definition and it was astouningly easy to read. :) So IMHO it's how often you tried the different programming style, what makes you thinking different about this. > > I also think that what looks simple or not depends on > the person. I myself have spent half of my life with > imperative languages. Mee too for many years, and "depends on the person" could also be said as: "depends on the experience of the person", and as I more and more get used to FP constructs, I see the problem solving diferently than some time ago. Ciao, Oliver ^ permalink raw reply [flat|nested] 44+ messages in thread
[parent not found: <Pine.LNX.4.63.0509251653340.9226@localhost.localdomain>]
* Ant: Re: [Caml-list] Avoiding shared data [not found] <Pine.LNX.4.63.0509251653340.9226@localhost.localdomain> @ 2005-09-26 21:29 ` Martin Chabr 0 siblings, 0 replies; 44+ messages in thread From: Martin Chabr @ 2005-09-26 21:29 UTC (permalink / raw) To: Brian Hurt; +Cc: caml-list Hello Brian, thank you for the whole lot of useful tips and nice explanations. It is immediately clear to me how to copy a record and a tuple, but I will need some time to think about your suggestions using a map. Intuitively I feel that it needs a method to create non-shared data structures in the first place, so that no copying whatsoever is needed. Regards, Martin --- Brian Hurt <bhurt@spnz.org> schrieb: > > > On Sun, 25 Sep 2005, Martin Chabr wrote: > > > Working with non-shared data structures in OCaml > > Deep copy of OCaml structures, marshaling > > ================================================ > > > > Dear group members, > > > > I need to process arrays of pairs of integers and > > records ((int * record) array) in which all > elements > > must be updated individually, which means that > > unshared data structures must be used. For arrays > of > > arrays I can produce unshared data by using the > > library functions Array.copy and Array.append to > > append the individual arrays into the embedding > array. > > It works, the low level arrays can be updated > > individually. But I cannot use the same scheme to > the > > array of the (int * record) structures, because I > do > > not know how to copy these structures to dissolve > the > > sharing. I do not even know how to copy records. > It > > seems to me that this problem occurs always when I > > want to produce an array of data with a fixed > > structure automatically (rather than entering the > > array [| ... |] by hand at the top level > interpreter > > using constants only). How can I produce > completely > > unshared structures? > > First of all, you can copy a structure easily enough > using the with key > word. Say you have: > > type my_struct = { > foo: int; > bar: float; > baz: string; > } > > You can write a copy function just like: > > let copy_my_struct x = { x with foo = x.foo };; > > In this case, the "with" construct says "create a > new structure with the > same elements as the old structure, except give the > foo member this new > value (which in this case just happens to be the > same value as the old > value)". Unfortunately you have to specify at least > one new value to use > the with construct, even if it doesn't get a new > value. This is > especially usefull if there are other value you want > to copy, for example, > you probably want to write: > > let copy_my_struct x = { x with baz = String.copy > x.baz } > > As this doesn't just create a new copy of the > structure, it also creates a > new copy of the string as well (foo and bar get > whatever values the > original structure had). > > I can create a new tuple just by breaking the old > tuple apart and putting > it back together again. For all two element tuples, > I can create a new > copy of them just by going: > > let copy_tuple (a,b) = a,b > > This function, given a tuple of two elements, > creates a new tuple with the > same two elements. > > The easiest way to create a new list from old values > is to just use the > map function. The output list type of List.map can > be the same type as > the input list. This is really usefull- imagine > that I want to add 1 each > to a list of floats, I can just write: > > List.map (fun i -> i + 1) lst > > So, to answer you specific question, say I have an > (int * my_struct) list > (where t is the structure I defined above). The > function to create a > whole new copy of this list (assuming I've written > copy_t like I did > above) is just: > > let copy_list lst = List.map (fun (i,s) -> i, > (copy_my_struct s)) lst > > Notice that I made copy_tuple an anonymous (unnamed) > function there. With > a couple of more tricks I can fit the whole thing on > one line: > > let cplst = List.map (fun (i,s) -> i, {s with baz = > String.copy s.baz}) > > Which is nice for bragging rights, but I kind of > like the more unpacked > version. > > Now, I'm going to jump from the question you asked > to the question you > didn't ask, and make a big assumption along the way. > I'd bet dollars to > donuts that you really don't want to be using either > lists or array- you > really want to be using a map. Let me guess: you've > written a function > like: > > let rec find i lst = (* find element i in list lst) > match lst with > | (j, s) :: t -> > if i == j then > s > else > find i t > | [] -> raise Not_found > > and are calling it a lot (or rewritting it a lot). > If this is the case, > then what you really want to be using is a map, not > a list or an array. > You can do this with the following code: > > module Int = struct > type t = int > let compare (x: int) y = > if x < y then > -1 > else if x > y then > 1 > else > 0 > end;; > > module IntMap = Map.Make(Int);; > > Now you can just keep your structures in a my_struct > IntMap.t. The find > function is now: > let find i lst = (* lst is a IntMap, not a list *) > IntMap.find i lst > ;; > > The big difference here is that IntMap.find is O(log > N) cost, while the > list-based find I wrote above is O(N) cost. What > this means is that if > the list is 1000 elements long, the list-based find > will have to do (on > aveage) about 500 steps of processing to find the > answer- it has to walk > halfway down the list. The IntMap.find function, > however, only has to do > about 10 steps on average to find the answer- it's > literally 50 times > faster to do the IntMap.find in this case than the > list-based find. If > we're dealing with a million elements, the > list-based find will take > 500,000 steps to find the answer on average, the > IntMap.find only 20, for > an incredible speed up of 25,000 times. > > Note that IntMap has a map function as well- so I > can steal the exact same > trick to copy it (but note that I don't have to > allocate the new tuples): > > let copy_list lst = (* list is an IntMap, not a list > *) > IntMap.map copy_my_struct lst > ;; > > If you actually want the associated integers as > well, use mapi instead of > map. > > Note that O(log N) vr.s O(N) thing applies to > updates as well. Let's say > === message truncated === ___________________________________________________________ Was denken Sie über E-Mail? Wir hören auf Ihre Meinung: http://surveylink.yahoo.com/wix/p0379378.aspx ^ permalink raw reply [flat|nested] 44+ messages in thread
end of thread, other threads:[~2005-10-06 16:54 UTC | newest] Thread overview: 44+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2005-09-25 21:32 Avoiding shared data Martin Chabr 2005-09-26 0:23 ` [Caml-list] " Bill Wood 2005-09-26 7:57 ` Claudio Sacerdoti Coen 2005-09-26 8:17 ` William Lovas 2005-09-26 21:07 ` Ant: " Martin Chabr 2005-09-26 22:08 ` Jon Harrop 2005-09-30 22:57 ` Oliver Bandel 2005-10-01 0:07 ` Pal-Kristian Engstad 2005-10-01 5:46 ` Bill Wood 2005-10-01 8:27 ` Wolfgang Lux 2005-10-01 18:02 ` Wolfgang Lux 2005-10-01 21:50 ` Ant: " Martin Chabr 2005-10-01 12:34 ` Oliver Bandel 2005-10-01 13:58 ` Bill Wood 2005-10-01 21:05 ` Ant: " Martin Chabr 2005-10-03 0:41 ` skaller 2005-10-03 1:13 ` Seth J. Fogarty 2005-10-03 13:09 ` Thomas Fischbacher 2005-10-03 14:57 ` skaller 2005-10-03 20:03 ` Ant: " Martin Chabr 2005-10-03 20:25 ` Thomas Fischbacher 2005-10-03 21:08 ` Jon Harrop 2005-10-04 18:06 ` Ant: " Martin Chabr 2005-10-04 18:32 ` Jon Harrop 2005-10-04 2:53 ` skaller 2005-10-04 16:15 ` Brian Hurt 2005-10-04 16:47 ` FP/IP and performance (in general) and Patterns... (Re: [Caml-list] Avoiding shared data) Oliver Bandel 2005-10-04 22:38 ` Michael Wohlwend 2005-10-05 0:31 ` Jon Harrop 2005-10-04 22:39 ` Christopher A. Watford 2005-10-04 23:14 ` Jon Harrop 2005-10-05 12:10 ` Oliver Bandel 2005-10-05 13:08 ` Jon Harrop 2005-10-05 15:28 ` skaller 2005-10-05 20:52 ` Ant: " Martin Chabr 2005-10-05 23:21 ` Markus Mottl 2005-10-06 16:54 ` brogoff 2005-10-05 0:45 ` Brian Hurt 2005-10-04 18:09 ` Ant: Re: Ant: Re: Ant: Re: Ant: Re: [Caml-list] Avoiding shared data Martin Chabr 2005-10-05 8:42 ` skaller 2005-10-05 11:14 ` Andrej Bauer 2005-10-01 21:36 ` Ant: Re: Ant: " Martin Chabr 2005-10-03 11:51 ` getting used to FP-programming (Re: Ant: Re: Ant: Re: [Caml-list] Avoiding shared data) Oliver Bandel [not found] <Pine.LNX.4.63.0509251653340.9226@localhost.localdomain> 2005-09-26 21:29 ` Ant: Re: [Caml-list] Avoiding shared data Martin Chabr
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).