Search code examples
ocamlarbitrary-precisionopamrational-number

Why are rational numbers from Num printed as <abstr>?


I continue with my exploration on the Num library of Ocaml, with the reason that one whole library about logics was written using it.

Today, I would like to make the negative of a rational number. Obtain -1/2, from 1/2.

To do so, I think that, given an a of type Ratio.ratio, I can compute the negative of it (and return a ratio, not a num) this way:

ratio_of_num (minus_num (num_of_ratio a))

(Functions from: https://ocaml.org/releases/4.05/htmlman/libref/Num.html#TYPEnum)

Now, I would like to check the result, but I always get this solution: Ratio.ratio = <abstr>

The point is that now I realize that I always get this solution when I use ratio_of_num. For instance:

ratio_of_num (Int 2);;
- : Ratio.ratio = <abstr>

I have searched a bit and found this question (OCaml toplevel output formatting) where a different function (ratio_of_int 2) was used, but seems no longer possible. Maybe that ratio is a different library.

Any help?

PS: By the way, in order to replace num in the future, I am trying to install Zarith with opam, but cannot.

My problem is I do opam install zarith and this is displayed:

┌─ The following actions failed
│ λ build conf-gmp 3
└─ 
╶─ No changes have been performed

The packages you requested declare the following system dependencies. Please
make sure they are installed before retrying:
    gmp

So I do opam install gmp and I get:

┌─ The following actions failed
│ λ build gmp 6.2.1
└─ 
╶─ No changes have been performed

Which offers me no clue on how to continue trying. Any help with this also?

I would appreciate any answer whether for the first question or the second one!!

Here below, I post some editions that have been added to the question, as a result of the conversation below:

EDIT (Solved adding the needed #require)

I have done what @ivg has suggested, but still does not work (I do the initial open Num, because it will ask it otherwise):


─( 23:12:59 )─< command 0 >──────────────────────────────────────{ counter: 0 }─
utop # open Num;;
─( 23:13:00 )─< command 1 >──────────────────────────────────────{ counter: 0 }─
utop # let pp_num ppf x = Format.fprintf ppf "%s" (Num.string_of_num x);;
val pp_num : Format.formatter -> num -> unit = <fun>
─( 23:14:11 )─< command 2 >──────────────────────────────────────{ counter: 0 }─
utop # #install_printer pp_num;;
─( 23:14:16 )─< command 3 >──────────────────────────────────────{ counter: 0 }─
utop # ratio_of_num (Int 2);;
- : Ratio.ratio = <abstr>

EDIT 2 (Also needed a #require)

I have also tried Ocaml instead of utop, but the error is worse:

        OCaml version 4.10.2

Findlib has been successfully loaded. Additional directives:
  #require "package";;      to load a package
  #list;;                   to list the available packages
  #camlp4o;;                to load camlp4 (standard syntax)
  #camlp4r;;                to load camlp4 (revised syntax)
  #predicates "p,q,...";;   to set these predicates
  Topfind.reset();;         to force that packages will be reloaded
  #thread;;                 to enable threads

# open Num;;
# let pp_num ppf x = Format.fprintf ppf "%s" (Num.string_of_num x);;
Error: Reference to undefined global `Num'
# 

EDIT 3 (Works in Ocaml, instead of utop)

##require "num";;
# let pp_num ppf x = Format.fprintf ppf "%s" (Num.string_of_num x);;
val pp_num : Format.formatter -> Num.num -> unit = <fun>
# #install_printer pp_num;;
# ratio_of_num (Int 2);;
- : Ratio.ratio = <ratio 2/1>
# 

EDIT 4 (Works in utop, note that printing simplifies the result when it is an integer)

utop # let pp_ratio ppf r = Format.fprintf ppf "%a" pp_num (num_of_ratio r);;
val pp_ratio : Format.formatter -> Ratio.ratio -> unit = <fun>
─( 23:28:07 )─< command 6 >──────────────────────────────────────{ counter: 0 }─
utop # #install_printer pp_ratio;;
─( 23:28:22 )─< command 7 >──────────────────────────────────────{ counter: 0 }─
utop # ratio_of_num (Int 2);;
- : Ratio.ratio = 2
─( 23:28:29 )─< command 8 >──────────────────────────────────────{ counter: 0 }─
utop # 


Solution

  • The reason why you have <abstr> instead of the actual representation is that the top-level (aka interpreter) doesn't know how to print the num object. It is easy to teach the top-level, using the #install_printer directive, e.g.,

    
    let pp_num ppf x = Format.fprintf ppf "%s" (Num.string_of_num x);;
    val pp_num : Format.formatter -> Num.num -> unit = <fun>
    # #install_printer pp_num;;
    # ratio_of_num (Int 2);;
    - : Ratio.ratio = <ratio 2/1>
    # 
    

    So we defined the pretty-printing function,

    let pp_num ppf x = Format.fprintf ppf "%s" (Num.string_of_num x)
    

    And then used the #install_printer directive to install it in the top-level,

    # #install_printer pp_num;;
    

    and now every time we have num it will be printed for us.

    You can also use this pp_num function together with other Format module functions (that are used for pretty printing), e.g.,

    Format.printf "my num = %a" pp_num (ratio_of_num (Int 2))
    

    It might be that an older version of OCaml is unable to derive how to print ratio from the nums itself, so we can help it by defining an additional printer,

    # let pp_ratio ppf r = Format.fprintf ppf "%a" pp_num (num_of_ratio r);;
    val pp_ratio : Format.formatter -> Ratio.ratio -> unit = <fun>
    # #install_printer pp_ratio;;
    # ratio_of_num (Int 2);;
    - : Ratio.ratio = 2
    

    Re: P.S.

    For zarith you need to install system dependencies. You can use opam for that, e.g.,

    opam depext --install zarith
    

    it will install the system dependencies (the gmp library) using your operating system package manager and then install the zarith library.