How does one print any type?

I just want to print any type (like a print statement in python). I am just learning ocaml using the ocaml command interactively with my script basically. How do I do this?

There is no such facility in OCaml. OCaml is not an interpreted language with a dynamic type system. When a program is compiled all types are erased, so it is impossible in runtime to reflect a value to its type. And yes, as you’ve already pointed out, when you interact with OCaml using interactive toplevel there is some generic printing facility, but this is a very special case because you’re not really running a program, but interactively compile it.

So, you have to accept this fact and learn how to program in OCaml using OCaml ways of doing things. First of all, learn how to use the Format.printf function. This is a generic function that takes the format specification and arguments and prints them, e.g.,

open Format

let () = 
  printf "Hello, %s world\n%!" "cruel";
  printf "Hello, %d times!\n%!" 42

As you can see, the special format specifiers, like %s or %d specify the type of an argument. So %d expects one argument of type int, and %s expects one of string, there is also %c for char , %b for bool and so on.

There is also a generic %a specifier, which expects not one argument, but two – a function, that will tell how to print the argument, and the argument itself. It is used to print values of abstract types, hence the name %a. There is a convention, that modules that define abstract types also provide a function called pp which specifies how this value should be printed, e.g.,

let () = printf "Hello, abstract student %a\n%!" Student.pp s

Where the Student module might be defined as

struct Student : sig 
   type t 
   val create : string -> int -> t
   val pp : Format.formatter -> t -> unit
end = struct 
   type t = {name : string; cls : int}
   let create name cls  = {name; cls}
   let pp ppf {name; cls} = 
      Format.fprintf ppf "%s of %d" name cls
end

That is roughly how we print everything in OCaml :slight_smile:

2 Likes

You may also use the ppx_deriving.show plugin from the ppx_deriving library. This plugin is able to generate 'a -> string and Format.formatter -> 'a -> unit (where 'a is the type you want to print) functions which can then be used for value printing.

type t =
{  number : int
;  text : string
}[@@deriving show]

This will produce show and pp functions of type t -> string and Format.formatter -> t -> unit, which can then be used as

let v = { number = 2; text = "Hello world"} in
print_endline (show v)

or

let v = { number = 2; text = "Hello world"} in
let s = Format.asprintf "%a" pp v in
print_endline s

Of course, you can always define such functions for the needed types by yourself.

As I can see, almost every library introducing a new type also provides pretty-printing (kinda pp in this example) functions out of the box.

1 Like

The same basic printing as the toplevel can be had with:

http://github.com/progman1/genprintlib (general value printing in compiled code)

Available through the official opam repository:
opam install genprint

Everything ivg@ wrote is right, but I thought I’d add a little bit of sugar (well, maybe it’s sugar – depends on your taste, I guess) to the medicine. Indeed, Ocaml is built on the philosophy that (aside from a few special spots) all the compile-time type information is erased at runtime (called “type erasure”). This is in stark contrast to Java, Python, and other languages, where every single object in the heap, has a “class object pointer” or equivalent. Wow, that’s really awful, isn’t it? This is also why Java, Ruby, Python and Golang (to a limited extent grin) can do runtime reflection, where Ocaml cannot.

Gosh, that’s awful. Well, the reason for this [and, I’d like to argue (and hope) the reason for this in all ML-family languages] is that we want for our runtime semantics to be as close to C as possible. That is, it should be possible to write Ocaml programs (and sure, you have to be aware of when allocation happens) and be able to think about them as if they’re C programs. Of course, ML-family languages are high-level languages. But we want the high-level expressivity at compile-time, with the "runs like C’ at runtime. No, it’s not perfect. Not at all. But that’s the goal. At least, I’d like to think that that’s the case.

In any case, the idea of “type erasure” was there at the beginning, so to speak, of all the ML-family languages, back in the 80s, maybe even in the 70s (I’m not old enough to answer for that).

So that’s why you won’t find runtime reflection in ML-family languages, except via what one might call “mirrors” (Gilad Bracha coined the term), and many of the ppx_{yadda,yadda} rewriters are, one might say, steps towards building a general “mirror” facility for types in Ocaml.

3 Likes

You can start the toplevel ocaml and then inside the toploop you can type:

#use "my_script.ml" ;;

Then you will get the signatures of the contents of your script.
You can also use the following command:

ocamlc -i my_script.ml

this will also print the signature of the contents of your script, which can also be used as an .mli file if it was a usable module.

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.

If you just want composable printers, there is the Fmt library by @dbuenzli.

1 Like