View Full Version : O'Caml style.
jemfinch
05-30-2002, 05:22 PM
Let's face it: O'Caml is a great language, but the syntax is horribly baroque. The syntax is really what keeps a lot of people from even trying the language. It's hard enough a mental burden to handle all the new strict typing issues, but then to have to fight with the syntax makes it even less probable that people will make it past 15 minutes with the language.
O'Caml does, however, have some great syntactical constructs. The "match" expression, for instance, is wonderful beyond comparison. So what I've done (and what you new O'Caml users might consider) is limit my code to a subset of O'Caml which is a bit prettier and more orthogonal than the full set of O'Caml syntactical constructs. Here's what I do:
First, before I show any code samples, I use a very standard variable naming scheme for ephemeral arguments (those arguments given to very short functions). They're just one letter, and serve pretty well for knowing immediately what arguments a function takes. Here they are:
f : function
l : list
i or n : int
x : float
s : string
This should make my code easier to read at first glance, because you'll be able to tell what type an argument is just by looking at it. I do, of course, use descriptive arguments in longer functions which have several of each type, or use an argument in more than one place, but in properly factored code (which should use a lot of very small functions instead of larger functions) the majority of my functions can use this naming scheme.
I never use function. I don't like it, it's a long keyword and it's confusing to new people because of the implied argument. I think arguments to functions often serve as documentation, and eliding such arguments by using function is in the end rather self defeating. Besides, you gain very little:
let rec map f l =
match l with
| [] -> []
| hd :: tl -> f hd :: map f tl
versus:
let rec map f = function
| [] -> []
| hd :: tl -> f hd :: map f tl
See? It doesn't really save much, and with syntax using the match statement, you can tell at a glance (a) how many arguments the function takes, and (b) the type of the arguments (since I use a standard argument naming scheme). The advantages of not using function by far outweigh the disadvantage of typing slightly more.
(If anyone is curious, I checked, and the code supplied for both those function definitions is exactly the same.)
On match statements, even though the first bar is optional, I always put it there. I think it makes the code look prettier and allows for less error-prone code modification if I end up moving the cases around or inserting another case before the first one.
Also on match statements, I always put the code after the arrow (->) in parentheses. This allows match statements within that block not to interfere with the first match statement and allows for easier code modification later on.
I'll think of others as I'm reminded of them :) Now, I have a Latin quiz to take :)
Jeremy
file13
05-30-2002, 06:07 PM
i couldn't agree more. i come from an Ada background (ya know, the "Software Enginner" types) and i am all for being able to read your code 2 days later. so when i do Ocaml i specifically write in a very explicit style. here's an example:
(* gemetria: returns a gemetria total given a word and a "translation" *)
(* *)
(* Usage: *)
(* val gemetria : string -> (char -> int) -> int *)
(* *)
(* where string is the word to translate and (char -> int) is the *)
(* translation. See enoch777 for an example of a translation table. *)
let gemetria (word) (translation) =
begin
let length = String.length (word) in
let total = ref 0 in
for i = 0 to length -1 do
let letter = String.get (word) (i) in
let number = translation (letter) in
total := !total + number;
done;
!total;
end;;
first off i put all parameters in parenthesis. i'm fully aware it's not necessary ecept in compund statements, but it's consistent and easy for human easy to parse. also, i always create function in the above style (no function or fun). next i ALWAYS use a begin..end in function. again, not necessary for the comiler, but it helps the reader see where the function begins and ends. it also makes it easy for neophytes to see when to use 1 semi colon or two.
then i fully comment the function, run it through the toplevel to get the val part (i.e.):
val gemetria : string -> (char -> int) -> int
and even go on to explain the params.
Ocaml is like Perl. it's very easy to write unreadable code. i'm not suggesting everyone do it the way i do, but i find this style to be very explicit and very clear.
Vince and i have butted heads over this in the past...he's Canadian ya knwo.... ;) i had this function like so:
let enoch777 (letter) =
begin
match letter with
'a' | 'A' -> 6
| 'b' | 'B' -> 1
| 'c' | 'C' | 'k' | 'K' -> 2
| 'd' | 'D' -> 4
| 'e' | 'E' -> 7
| 'f' | 'F' -> 5
| 'g' | 'G' | 'j' | 'J' -> 3
| 'h' | 'H' -> 10
| 'i' | 'I' | 'y' | 'Y' -> 9
| 'l' | 'L' -> 20
| 'm' | 'M' -> 8
| 'n' | 'N' -> 50
| 'o' | 'O' -> 70
| 'p' | 'P' -> 30
| 'q' | 'Q' -> 40
| 'r' | 'R' -> 80
| 's' | 'S' -> 200
| 't' | 'T' -> 300
| 'u' | 'U' | 'v' | 'V' | 'w' | 'W' -> 100
| 'x' | 'X' -> 60
| 'z' | 'Z' -> 90
| _ -> 0
end;;
and Vince asked me why i didn't do it like so: (i think it was like this but i don't have that email here):
let enoch777 (letter) = function
'a' | 'A' -> 6
| 'b' | 'B' -> 1
| 'c' | 'C' | 'k' | 'K' -> 2
| 'd' | 'D' -> 4
| 'e' | 'E' -> 7
| 'f' | 'F' -> 5
| 'g' | 'G' | 'j' | 'J' -> 3
| 'h' | 'H' -> 10
| 'i' | 'I' | 'y' | 'Y' -> 9
| 'l' | 'L' -> 20
| 'm' | 'M' -> 8
| 'n' | 'N' -> 50
| 'o' | 'O' -> 70
| 'p' | 'P' -> 30
| 'q' | 'Q' -> 40
| 'r' | 'R' -> 80
| 's' | 'S' -> 200
| 't' | 'T' -> 300
| 'u' | 'U' | 'v' | 'V' | 'w' | 'W' -> 100
| 'x' | 'X' -> 60
| 'z' | 'Z' -> 90
| _ -> 0;;
sure they both produce:
val enoch777 : 'a -> char -> int = <fun>
but i honestly find the more verbose version more explicit since and consistant. i want to make it clear that this function simply takes a char and send back a matching number. with Vinces version i personally have to look twice, crack a beer, and turn on the TV before it dawns on me what it does. sure it's "cooler." but i don't care about being cool. i want to come back to my code after a hard night at the pub and understand what the hell i did.... :)
hey everyone's got their own styles, but i belive that in a language like Ocaml it's imperative that people use a very explicit style--i feel the same way about Perl.
GnuVince
05-30-2002, 06:22 PM
I stopped using function yesturday when I saw one of Strike's function. I had:
let rec map f = function
[] -> []
| h::t -> (f h)::(map f t)
;;
Strike wrote it this way:
let rec map f l = match l with
[] -> []
| h::t -> (f h)::(map f t)
;;
This is just as short, but has the l argument.
file13 says he writes explicit names for function arguments; I only do so when it's not a very short function. If I take the above example, I could write:
let rec map a_function a_list = match a_list with
[] -> []
| head::tail -> (a_function head)::(map a_function tail)
;;
I think it just makes the coder "heavier" (french expression) for no reason. Of course, when you have a non-trivial function, using meaningful names is a must.
Another thing about match and with and others, do you use a pipe on the first line?
match l with
[] -> "Empty"
| _ -> "Not empty"
versus
match l with
| [] -> "Empty"
| _ -> "Not empty"
What are your thoughts guys? Of course, little differences like this won't prevent us from being able to read each other's code. I must also say that I don't like the begin/end;; usage of file13: it seems too much Pascal-like, procedural-like. This is only an opinion, but if you go in ocamlbrowser and check the implentation of the different modules, you do not see them using begin/end;; for functions.
file13
05-30-2002, 06:49 PM
yup. i'm fully aware the Ocaml developers don't use this kind of style. it drives me nuts sometimes trying to figure out their code. i remember playing with one of the CGI libs and the examples made my brain hurt.
yeah, the begin..end is kinda annoying. as i mentioned, i like Ada which looks like Pascal. :) so i think you can figure out my bias. but even in C/C++/Java/Perl they use definate characters to end expressions ({}). at the very least i'd encourage folks to use the end semicolons (;;) on their own lines so you can pick out the functions end easily.
GnuVince
05-30-2002, 09:07 PM
file13, but if I give you a function that doesn't have begin/end;; or arguments in parenthesis, you will understand, won't you?
file13
05-30-2002, 11:48 PM
when the tundra has melted into nuclear ash, remember the song....."Blame Canada" ;)
GnuVince
05-31-2002, 12:16 AM
I don't live in Canada! I'm from Quebec! Err... wait, Quebec is IN Canada... oh well. But don't blame Quebec: according to the rest of Canada we do nothing, so if we do nothing, nothing can be our fault! Blame Ontario!
jemfinch
05-31-2002, 02:00 AM
GnuVince: I answered your question about using the bar in the first match case in my original post :)
Jeremy
GnuVince
05-31-2002, 02:08 AM
Heh, yeah. Say Jeremy, still got those IRC bot sources around? If so, could you post them? Thanks a lot. I also remember that you were writing an interpreted language for extending the bot, you could post that as well. Thanks
jemfinch
05-31-2002, 02:22 AM
All my O'Caml source code is locked on my FreeBSD drive since I installed XP. I was about to send you a private message asking if I sent you the source code, and if so, if I could get it back :)
I do have Ocamlbot v. 0.1, but I'd really rather not release that code, it's so poorly factored that I'm embarrassed of it. I have some rather useful utility libraries for it, though, which I'd be happy to post. Some people might find my semi-translations of various Python modules rather useful.
Aside from that, the interpreted language I invented was, um, poor. I'd have to redo that before even considering releasing it.
Jeremy
GnuVince
05-31-2002, 02:33 AM
Heh! Anyways, if you can get these archives, just email them to me, I can probably put them on my webserver.
Strike
05-31-2002, 08:46 AM
jemfinch: I have your Python modules written in O'Caml if you want (post here or PM me)
PrBacterio
05-31-2002, 11:26 AM
Originally posted by GnuVince
Heh, yeah. Say Jeremy, still got those IRC bot sources around? If so, could you post them? Thanks a lot. I also remember that you were writing an interpreted language for extending the bot, you could post that as well. Thanks
Heh that sounds a lot like my ocamlbot. Its scriptable in Lisp which I wrote an interpreter for specifically for this purpose, though the Lisp interpreter source is really ugly, but Im currently in the process of rewriting it (the Lisp interpreter, not the whole bot) for also using it as game code language in a multiplayer game I'm currently writing with a friend of mine.
jemfinch
05-31-2002, 12:31 PM
Strike: you can go ahead and post them here in their own thread if you'd like. For some reason it appears have the compiled forms but not the .ml files themselves in my source tarball I got back from someone else.
Jeremy
GnuVince
05-31-2002, 12:40 PM
I still got your original tar.gz myself. And by the way, some functions in there are kindda useless ;)
let atof s =
float_of_string s;;
let atoi s =
int_of_string s;;
let lower s =
String.lowercase s;;
let upper s =
String.uppercase s;;
hehe :D
Strike
05-31-2002, 01:39 PM
Jemfinch, I can't post them here (far too big), but I will post a link.
http://www.engr.trinity.edu/~ddipaolo/python-modules-in-ocaml.tar.bz2 (0.98MB)
jemfinch
05-31-2002, 03:36 PM
GnuVince: Remember, I was replicating the Python string libraries, which meant a little bit of duplicated functionality :)
Jeremy
vBulletin® v3.7.0, Copyright ©2000-2009, Jelsoft Enterprises Ltd.