Constructive criticism

Pierre Weis (weis@pauillac.inria.fr)
Tue, 29 Aug 1995 19:55:24 +0200 (MET DST)

From: Pierre Weis <weis@pauillac.inria.fr>
Message-Id: <199508291755.TAA11490@pauillac.inria.fr>
Subject: Constructive criticism
To: caml-list@pauillac.inria.fr
Date: Tue, 29 Aug 1995 19:55:24 +0200 (MET DST)

I've taken the time to learn caml-light, and I have found it to have
been well worth the time. I have a few comments about version 0.7.

Positive comments:
- I generally like it and intend to use it wherever possible
- There are several features I especially like
- ability to use pointers when you really really want to
- ability to use arrays and strings relatively cleanly
- the general operators like
"store arbitary type to disk"
"make a hash table for type X".
- It seems reasonably efficient and correct.
- I've looked at many other functional languages and I like caml-light
the most.
- Thanks for making this available.

Constructive criticism (hopefully not just me not noticing something obvious):

- The reference manual, under the section
"streams, parsers and printers" seems to ignore parsers and printers.
They are described elsewhere...it is not a problem, just an
annoyance.

- The following error seems difficult to understand:

#let testit = fun | 4 -> 2 | 1 | 2 -> 3;;
Toplevel input:
>let testit = fun | 4 -> 2 | 1 | 2 -> 3;;
> ^
Syntax error.
#

Why is this OK with "function" but not with "fun"? It is
not possible to match something to the symbol "|" is it?

- There seems to be a bug in the string_of_num routine in
the contributed libnum package:

#let x = num_of_int 1024;;
x : num = Int 1024
#let xx = mult_num x x;;
xx : num = Int 1048576
#let xxx = mult_num xx x;;
xxx : num = Big_int <abstr>
#string_of_num xxx;;
- : string = "1737418240"

when the result should be "1073741824".

- The standard library seems to miss some routines that
I would expect / like to see through symmetry (for instance, I would
like to have the same functions available for vectors as for lists,
whenever such a thing makes sense.

Why? Because one would ever use something often enough with lists
to justify its inclusion in the core library, then it is probably also
useful for vectors, and if they have similar names
(such as it_list -> it_vect) one does not have to learn anything new
- it extends the functionality of the standard library without
making it any harder for a user to rememebr what things do. In
particular, it_vect2 could be used to implement a vector dot product
as discussed a while ago in the mailing list.

Some other routines that I expected that were not present:
sprintf ( given so many other printf variants )
copy_string ( given copy_vect )

Other routines that I have needed for my personal standard
library that are perhaps of wider use are:

let vect_change f v =
for i = 0 to (vect_length v) -1 do v.(i) <- f v.(i) done;;

An integer modulus, division and gcd that give known
answers for negative arguements.

let list_of_n n x = list_of_vect (make_vect n x);;

let map_list_count f l =
let rec work n = function
| [] -> []
| h::t -> (f n h)::(work (n+1) t)
in work 0 l
;;

let map_vect_count f v =
vect_of_list (maplistcount f (list_of_vect v));;

let rec list_do f = function
| [] -> ()
| h::t -> list_do f t; f h; ()
;;

let rec extract_list f = function
| [] -> []
| h::t -> let r = extract_list f t in if f h then h::r else r
;;

let rec create_numlist from upto =
if from=upto
then [from]
else from::(create_numlist (from+1) upto)
;;

In particular, map_vect_count is really difficult to
implement without going via lists because one needs to initialise
the list to something of the appropriate type.

Probably futile wish list for implementors with infinite amounts of time.

- Packed arrays of enumerated (sum) types with only a small number of
possibilities and with no arguements -- like boolean.

- I would like to be able to not have to bracket the unary minus
before a constant in a function application.

eg. let m1 = num_of_int -1;;

- I would like to be able to use a symbol twice in
pattern matching. For instance,

let compare = fun
| x x -> 0
| x y -> if x>y then 1 else -1
;;

I realise that this involves (in some cases) a generalised
comparison which is not always possible for functions, but a
generalised "=" is used elsewhere, and the exception for functional
values seems like a reasonable solution.

- I would like someway of handling the following nicely:

| a b when let (success,res) = big_fn a b in success
-> let (success,res) = big_fn a b in res

Perhaps something like

| a b -> let (success,res) = big_fn a b in
if success then res else fail

where "fail" would pass one on to the next condition to be tested.

- I would like to be able to write functions that are in some
way symmetric between parameters. For instance, instead of writing

fun
| Null x -> x
| x Null -> x
| x y -> messy_implementation_of_add x y

I would like to be able to merge the two first lines into one line.
I can't suggest a nice syntax for this that makes sense for more
than two parameters and which is still intuitive. This problem
is made less of a problem if the next point is done:

- In a similar vein to the previous point, it would be nice to
be able to have multiple matches have bindings, so the
previous example would become:

fun
| Null x
| x Null
-> x
| x y -> messy_implementation_of_add x y

I have a real example with four bindings that all end up executing the
same code to the right of an "->" in a given match, and it would have
been very nice to be able to not repeat it four times, and there are
enough elements in the binding to make a function call messy.

- I would like to be able to define a new structure instance with
a declaration like { x ; Field3=5 ; Field7 = 9 } which would make a
copy of the structure x, whilst changing the two given fields to the
appropriate values. Why? So I can write a program that has lots of
things that it will eventually do with slight changes to a structure,
and I don't have to go through and change routines that are
irrelevant to the change.

Really grasping at straws:

- I would like to be able to perform "function overloading" as
in C++. Furthurmore, greedy person that I am, I would like this
overloaded-ability to automagically be passed through into
functions that use the overloaded function (which I realise
is very difficult, and would either require multiple object code
for each possible operator combination, or an extra, "invisible"
function parameter to be passed to the function when the type
resolution is finally done.) I realise that this is especially
difficult in a multiple module context, and that assigning a type
to such a function would be difficult, but I still mention it
as I would really like it. Maybe I should go to caml rather than
caml-light.

Andrew Conway
arc@labri.u-bordeaux.fr