I had wondered:
Is it “poor taste” to include type information in function definitions?
It seems like very nice documentation, but then again, maybe it is nicer rely on the inferencing engine to reduce the amount of code?
In theory you write short, easy to understand functions; in practice it isn’t always this simple though.
People kindly replied:
When you can avoid it, it is better to NOT include type information. Using a tool like “ocamldoc” will render the function and its inferred types in a real documentation format (e.g. HTML).
There are cases where you need to include type information, but they are corner cases.
Interface files (.mli) are usually the main place for documentation. Within the .ml files, you can explain your algorithms but it’s usually just plain comments, not so much type annotations.
As to the matter of “taste”, note that this practice is not idiomatic in Ocaml, in contrast to Haskell where it seems current. I’m not sure if that’s your case, but people coming to Ocaml from Haskell may at first tend to exaggerate on this aspect…
2 thoughts on “Is it "poor taste" to include type information in function definitions?”
It is poor taste to use type information instead of good variable names. I once read in an ant simulation program : “let move (x : ant) (p : position) = …”.
Type information is often used when debugging : if your code doesn’t compile because of a type error, it is sometimes difficult to understand the exact cause of the type conflict. Adding explicit type annotations provides you guarantee that “if that function checks, then I’m sure that at this point, the type is …”, wich are useful to track down the exact source of the error.
After the type problem have been fixed, most programmers guiltily remove such type annotations, like you would remove an inelegant (prerr_endline “it’s all fine here”). Some don’t.
It is also sometimes useful to restrict a type to be less general than the one inferred. If the general type is more polymorphic that what you know you’ll need, it can be useful to restrict it.
For example, the simplest way to code List.iter is inferred type ((‘a -> ‘b) -> ‘a list -> unit), and it’s usually better if the compiler stops you to use it with a function that doesn’t return unit.
Of course, even in that case, it is better to put such restrictions in the .mli file (here, it would be in the .mli file of the List library), but sometimes it’s about auxiliary functions wich are local to the module.
Lastly, beware that “let f (x : ‘a -> ‘b) = ..” doesn’t actually enforce polymorphism : any inferred type that is less general than (‘a -> ‘b) will be accepted for x (eg. “let f (x : ‘a -> ‘b) = x 1”). Only module signatures, such that .mli declarations, enforce polymorphism.