OCaml is a very big and complex language, like C++, and many parts
ofit kinda stick out, are experiments that didn't go well, or exist
only for backward compatibility.
If you want to code OCaml, you might find these hints useful.
- "Exceptions"
in OCaml won't give you a backtrace. This is easily one of the top 3
worst things about OCaml. So even in situations when you'd use an
exception in Ruby/Python/Java/etc., think whether you can do something
else in OCaml.
- For handling "can't happen" situations use something like failwith (sprintf "Internal error: function foo(%d, %s, %s) expects a non-empty list" a b (foo_to_string c)). Because you won't get a backtrace, you should include as much information as possible.
- OCaml's standard library is one of its weakest parts. Always use .
If you are concerned about people introducing unnecessary dependencies,
simply copy sources of it to your repository with an appropriate
copyright notice.
- As we know from Perl/Ruby/Python/every
other modern language, the three most common data structures you ever
use are strings, hash tables and resizable arrays. You can get OCaml
support all of them reasonably well, but it doesn't by default.
- sprintf
is one of the most useful OCaml functions. And do open Printf in all
your OCaml files, it will save you a lot of typing and functions from
Printf module have names that don't collide with anything. If you need
to print something for debugging, define converters of all your types,
like foo_to_string foo = sprintf "%s %d %s" foo.a foo.b foo.c
- DynArray
(resizable arrays) is one of the most useful OCaml data types ever. No
more idiocy like building list ref and reversing it.
- OCaml
Hashtbl is weird and confusing. It is used for 1->1 hashes and
1->N hashes (and ('a,unit) hash tables are used as sets) and it's
easy to get something wrong (was Hashtbl.add meant to add another
element to 1->N hash or was it simply setting a value that didn't
exist before ...). Besides, calling Hashtbl.blah is extremely verbose
and it's impossible to include them in your namespace due to
collisions. So it's very good idea to make types ('a,'b) ht = HT of ('a,'b) Hashtbl.t for 1->1 hash tables, ('a,'b) mht = MHT of ('a,'b) Hashtbl.t for 1->N hash tables and 'a set = SET of ('a,unit) Hashtbl.t.
Then define functions like like ht_set, ht_get, mht_add, mht_get_all,
ht_iter, mht_iter_all, set_add, set_mem etc. in a module that you
include from all your files (I usually call it util.ml). And definitely
define functions like ht_keys, ht_values, ht_iter_keys etc. I have no
idea how could they have forgetten them in the std library.
- Do
not use tuples bigger than 2 (in special cases 3) elements, or
long-living tuples. Use records instead. Records can be easily
extended, can be made mulable, and you won't have to remember which
field of the tuple meant what. This also applies to Python ;-)
- Encapsulate
all common folds and tail recursions. It's very easy to make a mistake
with them and the code looks ugly. On the other hand high-level
functions like map, iter, filter, collect, join etc. don't clutter your
code. Never use the same recursion/folding pattern twice. Extract the
pattern. I think it's a good idea to separate the pattern from the
application code for single-use patterns too. Some examples: collect : ('a'->'b option) -> 'a list -> 'b list, mht_iter_all : ('a' -> 'b list -> unit) -> ('a,'b) mht -> unit.
- ocamldep
is pretty helpful for writing Makefiles. Simply do ocamldep *
>>.deps from time to time and include .deps from your Makefile.
阅读(2271) | 评论(0) | 转发(0) |