How can OCaml values be printed outside the toplevel?
Asked Answered
A

4

20

The OCaml repl ("toplevel") has rich printing for any types, user-defined or otherwise. Is it possible to access this functionality outside the toplevel, rather than having to write an entirely custom set of value printers for one's own entire set of types?

Ain answered 31/8, 2011 at 17:55 Comment(3)
related: discuss.ocaml.org/t/how-does-one-print-any-type/4362/12Danelledanete
related: quora qr.ae/pGu2yIDanelledanete
if you don't know how to print in top level (and looking to print in ANYWAY see this: discuss.ocaml.org/t/how-does-one-print-any-type/4362/…). Basically load the file in toplevel/utop #use "file.ml";; and then call the variable var;; in toplevel.Danelledanete
J
11

The pretty-printing facility is part of the toplevel library. You'll find the source in toplevel/genprintval.ml. It's understandable, considering that it needs type information: you can't just throw any value at it, the choice of pretty-printer is based on the type.

If you want to use this code in your program, you'll need to link with the toplevel library (toplevellib.cma) or compile in genprintval (which means bringing in enough bits of the type checker to analyse the type, it can get pretty big).

There is a similar facility (but not sharing the code, I think) in the debugger (debugger/printval.ml and debugger/loadprinter.ml).

There are third-party libraries that you can directly link against and that provide pretty-printing facilities. Extlib's Std.dump provides a very crude facility (not based on the type). Deriving by Jeremy Yallop and Jake Donham is another approach. This Caml Weekly News item offers more suggestions.

Jolynjolynn answered 31/8, 2011 at 22:49 Comment(2)
@newacct Yes, you need the type information, which is discarded by the compiler (but is available to the toplevel or the debugger). Third-party libraries provide lightweight ways to write a type-specific pretty-printer for every type, not a way to write a generic type-dependent pretty-printer. There are language extensions that permit generics, but no production-ready implementation AFAIK.Illumine
btw at present, at least, Extlib.Std.dump and BatPervasives.dump, mentioned by @newacct, are the same function: Extlib.Std.dump == BatPervasives.dump;; => bool = true.Eolithic
B
10

The OCaml Batteries Included library contains the dump function in its BatPervasives module . It converts any value to a string and returns it. You can see its source code here. The output will not be identical to the toplevel, because some information is lost at runtime, e.g. abstract data type constructors will become integers.

Brocket answered 31/8, 2011 at 21:57 Comment(0)
F
3

No. As of OCaml 4.06, the compiler doesn't make type information available at runtime. It is therefore not possible to have standalone programs that nicely print any OCaml data without some compromises. The two main avenues are:

  1. Some form of preprocessing which derives printers from type definitions. Today, the best approach might be the show plugin of ppx-deriving. This requires annotating each type definition.
  2. Relying only on the runtime representation of values. This requires no effort from the programmer and works out-of-the-box on data produced by external libraries. However it doesn't show things like record field names or any other information that was lost during compilation. An instance of this approach is detailed below.

The function Dum.to_stdout from the dum package will take any OCaml value, including cyclic ones, and print their physical representation in a human-readable form given the data available at runtime only.

Simple things give more or less what one would expect:

# Dum.to_stdout ("Hello", 42, Some `Thing, [1;2;3]);;
("Hello" 42 (582416334) [ 1 2 3 ])

Cyclic—and in general, shared—values are shown using labels and references. This is a circular list:

# let rec cyc = 1 :: 2 :: cyc;;
# Dum.to_stdout cyc;;
#0: (1 (2 #0))

We can also look into the runtime representation of functions, modules and other things. For example, the Filename module can be inspected as follows:

# module type Filename = module type of Filename;;
# Dum.to_stdout (module Filename : Filename);;
(
  #0: "."
  ".."
  #1: "/"
  #2: closure (#1 #3: closure ())
  #4: closure ()
  closure (#4)
  closure ()
  closure ()
  closure (#5: closure (#3))
  closure (#5)
  closure (#5)
  closure (closure () #3 #0)
  closure (closure () #3 #0)
  closure (#6: closure (#2 <lazy>) #7: (#8))
  closure (#6 #7)
  closure (#7)
  closure (#7)
  #8: "/tmp"
  closure (closure () "'\\''")
)
Fester answered 13/6, 2018 at 20:47 Comment(2)
how does one install the dum package in ocaml?Danelledanete
@CharlieParker with opam like for most ocaml libraries: opam install dumFester
D
-1

I know you want it outside of top level but I think it's worth mentioning how to do it in top level so that ppl looking for printing in anyway (since it seems outside top level is not trivial):

  1. load your file in top level
utop
#use "datatypes.ml";;
  1. then "call" the variable inside top level:
utop # let nada = Nothing;;
utop # nada;;
- : foo = Nothing

ref: https://discuss.ocaml.org/t/how-does-one-print-any-type/4362/16?u=brando90

Danelledanete answered 31/8, 2011 at 17:55 Comment(1)
if you don't know how to print in top level (and looking to print in ANYWAY see this: discuss.ocaml.org/t/how-does-one-print-any-type/4362/…). Basically load the file in toplevel/utop #use "file.ml";; and then call the variable var;; in toplevel.Danelledanete

© 2022 - 2024 — McMap. All rights reserved.