As an additional piece of information, Batteries has to be mentioned.
Batteries is a superset of the standard library which, amongst other things, offers composable printers for every types, so that it is often not necessary to devise custom printers for your own types.
For instance, if you had this type:
type t = (string, (float * int) list) Hashtbl.t
then a printer for this would be:
let print output_chan value =
Hashtbl.print String.print (List.print (Tuple2 Float.print Int.print)) output_chan value
Basically the kind of code that the ppx_deriving extension would generate, with the advantage that you can more easily substitute part of it with some custom printers of your own.