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?

5 Likes

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:

15 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.

4 Likes

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

2 Likes

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.

8 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.

1 Like

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.

1 Like

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

1 Like

One way to do this is to iterate over the runtime structure. You can get quite good information out of this, although it’s not a complete view of the compile time types by any means. I wrote a utility to do this called Dumper: https://web.archive.org/web/20101101234304/http://merjis.com/developers/dumper

It’s pure OCaml and doesn’t require any libraries, weird syntax, modifications to your source code or anything like that.

2 Likes

Reflection might be impossible, but why would that make printing impossible?

In other words, I agree that type erasure prevents you from having a fully polymorphic print: 'a -> string, but that’s not necessarily what you want. For example, wouldn’t it work to add a print primitive introducing a '_a constraint (like `ref, so you would only be able to use it monomorphically), which the compiler would then replace at compile time with a type-specialized printer?

(I think that would handle most of the cases that I care about for debugging, and AFAICT it would be no more restrictive than the ppx_deriving approach, though not as customizable.)

Am I missing something?

but why would that make printing impossible?

it’s perfectly feasible. I did a compiler variant that captured the type
info needed just for printing and injected it into the parsetree for such printing at runtime. then it turned out that the same could be achieved with a compiler plugin but plugin support was dropped from 4.0.9 so I didn’t release it.
then it became apparent to me that .cmt files could be used to
extract the type info and there is a release at GitHub - progman1/genprintlib: general value printing within compiled programs.
the downside is that it needs help finding .cmt locations via env variable setting and a ppx extension to hide the runtime lookup.
…and it only works in compiled code, not as an interpreted toplevel phrase.
so a really ugly, unappealing compromise!

that would handle most of the cases that I care about for debugging

it seems to me that such a lightweight debugging tool is a good rationale for inclusion in the compiler. my variant does it with zero slow down.
it seems incredible now but I looked for this in ML 30 years ago. what other language (high level) compiler doesn’t have this!

your mentioning '_a syntax for monomorphism has been a reminder that I can use to go back and simplify my implementation. thanks!

2 Likes

What I want is the simplest and quickest thing… I am just learning OCaml - not developing anything real. I am also trying to compare it with Coq. What I really want is to do something similar to Coq’s Compute e.g.

  Inductive nat_list : Type :=
  | NatNil : nat_list
  | NatCons : nat -> nat_list -> nat_list.

  Compute NatCons 1 (NatCons 2 NatNil).

Which just displays that the inductive data types have been defined correctly. I am just playing around. What is the simplest way for me to print things when I run my playground script? Ideally something like:

OcamlCompute NatCons 1 (NatCons 2 NatNil).

displays:

     = NatCons 1 (NatCons 2 NatNil)
     : nat_list

Print for me:

utop # IntCons (1, IntNil);;
- : int_list = IntCons (1, IntNil)

Right now I am just copying pasting from my script to utop…


related:

As as approximation to what you are looking for, you could try piping your script file to utop/ocaml instead of passing them as filename arguments.

The toplevels do different things depending on whether the files are passed via standard input or through a filename argument.

You want to learn how to use ppx_deriving show — it is the easiest way to construct printer functions for debugging quickly.

1 Like

this seems complicated. I still don’t get why I’d need to do this and it’s not just a feature out of the box for debugging. I don’t need to learn about complicated print functions for other languages, why do I need to for OCAML?

Can you provide a small full example? e.g. here is a type you can use:

type foo =
  | Nothing (* Constnat *)
  | Int of int (* Int constructor with value Int int *)
  | Pair of int * int (* Pair constructor with value as pair of ints *)
  | String of string;; (* constructor String that has the value string *)

let nada = Nothing;;
(*
# let nada = Nothing;; declares a variable nada of type foo with value Nothing
val nada : foo = Nothing
*)
let x = Int 1;;
(*
# let x = Int 1;; declares variable x of type foo with value Int 1
val x : foo = Int 1
*)
let pair12 = Pair (1, 2);;
let hello = String "Hello!!!";;

you can load the file and then just “call the variable with the type and value you want”. e.g.

type foo =
  | Nothing (* Constnat *)
  | Int of int (* Int constructor with value Int int *)
  | Pair of int * int (* Pair constructor with value as pair of ints *)
  | String of string;; (* constructor String that has the value string *)

let nada = Nothing;;

then:

utop

then:

 #use "datatypes.ml";;
let nada = Nothing;;

then that calls

nada;;
- : foo = Nothing

if you don’t know how to print in top level (and looking to print in ANYWAY see this: How does one print any type? - #16 by brando90). Basically load the file in toplevel/utop #use "file.ml";; and then call the variable var;; in toplevel.

see: pretty print - How can OCaml values be printed outside the toplevel? - Stack Overflow

1 Like

Here is a scenario where pretty printing an arbitrary type can be enormously useful during debugging

...
match something with
| ...
| _ -> raise (Failure "some mysterious error, what did I miss?") 

If you could get it to run under the repl, here is what you could do

let offender = ref someDummyValue
match something with
| ...
| mysterious -> let (_ = (offender := mysterious)) in raise (Failure "some mysterious error, what did I miss?") 

Then you can use !offender in the toplevel and inspect the mysterious offender!

1 Like

Your solution only works when the type of offender is monomorphic. For such a case, I use the following method:

  1. define a function let watch_x (x : type_of_mysterious) = ()
  2. then on that branch:
mysterious -> watch_x mysterious; raise (Failure "some mysterious error, what did I miss?") 
  1. in the toplevel, #trace watch_x;;

So when I run the code, I get a trace of the values of mysterious. Obviously, in your example, we’ll only get one, but in general, I want to do this in situations where I don’t know what the error is, so really I’m getting a trace of the arguments or values of variables or whatever.

Basically, the only time I use a debugger is when I’m writing Perl (b/c Perl scripts are simple enough that they seem to work adequately). Otherwise, it’s this sort of tracing.

4 Likes