From mboxrd@z Thu Jan 1 00:00:00 1970 Received: (from majordomo@localhost) by pauillac.inria.fr (8.7.6/8.7.3) id XAA01065; Sat, 26 Apr 2003 23:43:27 +0200 (MET DST) X-Authentication-Warning: pauillac.inria.fr: majordomo set sender to owner-caml-list@pauillac.inria.fr using -f Received: from concorde.inria.fr (concorde.inria.fr [192.93.2.39]) by pauillac.inria.fr (8.7.6/8.7.3) with ESMTP id XAA01201 for ; Sat, 26 Apr 2003 23:43:26 +0200 (MET DST) Received: from epexch01.qlogic.org ([63.170.40.3]) by concorde.inria.fr (8.11.1/8.11.1) with ESMTP id h3QLhOH25182 for ; Sat, 26 Apr 2003 23:43:25 +0200 (MET DST) Received: from epmailtmp.qlogic.org ([10.20.33.254]) by epexch01.qlogic.org with Microsoft SMTPSVC(5.0.2195.5329); Sat, 26 Apr 2003 16:42:46 -0500 Received: from [10.20.33.146] ([10.20.33.146]) by epmailtmp.qlogic.org with Microsoft SMTPSVC(5.0.2195.4905); Sat, 26 Apr 2003 16:42:45 -0500 Date: Sat, 26 Apr 2003 16:51:11 -0500 (CDT) From: Brian Hurt X-X-Sender: Reply-To: Brian Hurt To: Siegfried Gonzi cc: Brian Hurt , Ocaml Mailing List Subject: Re: [Caml-list] Easy solution in OCaml? In-Reply-To: <3EAA8D78.8000006@stud.uni-graz.at> Message-ID: MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII X-OriginalArrivalTime: 26 Apr 2003 21:42:45.0699 (UTC) FILETIME=[C60FA130:01C30C3C] X-Spam: no; 0.00; qlogic:01 caml-list:01 siegfried:01 gonzi:01 bug:01 python:01 0.0:01 fullfill:01 passing:01 floats:01 foo:01 recursion:01 warns:01 hacks:01 goody:01 Sender: owner-caml-list@pauillac.inria.fr Precedence: bulk On Sat, 26 Apr 2003, Siegfried Gonzi wrote: > >I find that if you're working against the compiler, either a) you haven't > >thought the problem through, b) there's an easy solution you're missing, > >or c) you have a bug. > > > > I think this is a very interesting question. What I love about Scheme > (or even Python lists) is the fact that you can put into a list what you > want. I had a problem the other day which was as follows: > > I have a Scheme function which extracts floating-point values from > strings located in a file: > > == > 12.33,43.4,4.56,nan,1.23 > 23.3,34.4,nan,1.2,0 > ... > == > > The extracted floating point numbers become stored into an array: > > (vector (vector 12.33 43.4 4.56 -1.0 1.23) (vector 23.3 34.4 -1.0 1.2 0.0)) > > My boss gave me a file which he urgently had to evaluate in order to > fullfill a time schedule (some measurements from the field of > experimental physics). But there was a problem, because the file was as > follows: > > == > name1,2.23,2.23,23.4 > name2,23.34,23.34,.223 > ... > == > > The first entry was an annotation but my Scheme function expects a > string which can be converted to a floating point number. But Scheme is > your friend here, because one more line in my file-reading function and > you get something like this: > > ((name1 2.23 2.334) (name2 3.34 23.2 ...)) > > In Ocaml I would have to skip the first entry because it is not a > floating-point value. All my other functions were not affected, because > passing around arrays or lists does not mean you must put forward > floating-points or string arrays or whatever. You have to skip the first entry anyways, because it's not a floating point value. Or you have to have special handling for strings and floats seperately- every time you pull an element out of the list, you need to go "is this a string or a float?" and handle it correctly. Otherwise, please define what "name1" /. 3.0 should be. How you put strings and floats into the same list is you define a type, call it foo_t for the moment: type foo_t = String of string | Float of float ;; And make it a list of foo_t's. You can now pick apart your list by, for example: let print_list lst = let rec loop lst = match lst with [] -> () (* End the recursion *) | String(s) :: t -> print_string (s ^ " "); loop t | Float(x) :: t -> print_string ((string_of_float x) ^ " "); loop t in print_string "( "; loop lst; print_string ")\n" Now, let's say at a later point you want integers in your string as well, so you extend foo_t to be: type foo_t = String of string | Float of float | Int of int ;; But being a forgetfull sort (like I am all the time), you forget to update print_string. The next time you try to compile, ocaml tells you: File "temp.ml", line 10, characters 8-274: Warning: this pattern-matching is not exhaustive. Here is an example of a value that is not matched: Int _::_ You constantly see (or at least I constantly see) the same pattern repeated over and over- pulling an Object out of a data structure and then running it through a tree of if typeofs to figure out what sort of creature you just pulled out. And when you add a new type to the structure, all those if trees need a new branch. In Ocaml, you can do the same thing, but the compiler checks your work and warns you if you forgot a place. If the string is always just the first element, you might consider using a tuple or structure. But the short form is, you have a problem. And one the language shouldn't just let you sweep under the rug, as we'll see. > > I agree upon that the above feature can sometimes lead to bad hacks, > because you the return value of a function can consist of a list where > you put things into the list which you decide later on whether you want > augment the list by other parameters, for example: > > Your first version of the function has as return value: (list (list 2 3 4)) > > A year later you decide you want something like this: (list (list 2 3 4) > (vector 3 4 5) "hi guy") > > The goody here is all your other functions which expect the output of > the above function must not be re-written, as opposed to Clean or OCaml > where you would have to re-write all your functions because the > structure of your return-list has changed. If you don't know what you just pulled out of a list, how can you do anything with it? About all you can do with it is to move it around. Take it out of one data structure and put it into another. Or pass it to some other function. Which you can quite happily do with Ocaml- take a look at List.map or Array.of_list in the standard libraries for examples. But once you want to do something more with the elements than just move them around, you need to know what they are- what type they are. So let's say I'm expecting that I'm dealing with an int list list (your first example there). I could easily convert this to a float list list by doing: let conv lst = List.map (fun l -> List.map (float_of_int) l) lst With some suitable generic handling, instead of calling List.map, I just call map, so when I hit an array of ints instead of a list of ints, I call Array.map and not List.map. It could be done, it'd be of limited use but it could be done. But what am I supposed to do with the string "hi guy"? Should it treat the string as a list of char, implicitly convert it to a list of int, and then convert the list of int to a list of float, returning [104.; 105.; 32.; 103.; 117.; 121.]? What sensible thing can you do here? This is what I meant about a bug in your program or not thinking the design through. If there is a reason you're putting a string into that list, it means something. Decide what it means, and at each point you handle elements from that list, decide what to do if you see a string- even if it's just "ignore it and go on", think about it and decide. > > >This is usefull for two reasons, in my experience: sometimes, it lets the > >compiler produce better code (for example, consider the function: > >let foo x y = x == y > >If you know that x and y will always be integers, doing: > >let foo (x:int) (y:int) = x == y > >allows the compiler to inline integer equals, rather than calling the > >generic compare function- much faster). > > > I doubt that type correctness is always better or leads to failure-free > program execution. Type correctness is not a panacea- bugs can still sneak past. Not even proofs of code correctness is good enough- they just prove the code does what it's designed to do, not what it's *supposed* to do. Unfortunately, no CPU architecture I know of includes a DWIM instruction (despite long standing and unremitting demand)... Type correctness does catch a lot of bugs, often even surprisingly deep bugs. And every bug the compiler catches is one less you have to hunt down yourself... > Yesterday I faced the following situation: dividing > the two vectors: > > (vector 0.0 0.0 23.34 23.4) > through > (vector 0.0 0.0 0.0 23.4) > > I forgot to check that division by zero is not a good idea, but the good > old Bigloo compiler didn't rebel and had as output: > > (vector #f #f #f 1.0) > > Maybe a bad example because in OCaml you could use exceptions or > something like this, but in this case the OCaml program had aborted > (this is also true for C,...). I am not sure how long it would have gone > good, but the Scheme program had not aborted in a safety-critical > system-environment. I am really often surprised how forgiving Scheme and > also CommonLisp actually are in such situations. Hmm? What platform are you on? Linux on x86: $ ocaml Objective Caml version 3.06 # 1. /. 0. ;; - : float = inf. # 0. /. 0. ;; - : float = nan. # 0. /. 1. ;; - : float = 0. # let x = [ 0.0 ; 1.0 ; 0.0 ; 1.0 ] and y = [ 0.0 ; 0.0 ; 1.0 ; 1.0 ] ;; val x : float list = [0.; 1.; 0.; 1.] val y : float list = [0.; 0.; 1.; 1.] # let rec vdiv a b = match a, b with [], [] -> [] | x :: at, y :: bt -> (x /. y) :: (vdiv at bt) | _ -> assert false ;; val vdiv : float list -> float list -> float list = # vdiv x y ;; - : float list = [nan.; inf.; 0.; 1.] # No exceptions for me. Brian ------------------- To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/ Beginner's list: http://groups.yahoo.com/group/ocaml_beginners