How to print the value of type int option

let hs = [5;4;3;6;7];;

let rec fetch_xs_max = function
    | []     -> None
    | x::xs  -> match fetch_xs_max xs with
                | None      -> Some x
                | Some m    -> Some (max x m);;  
                                                                                                                  
Printf.printf "%d\n" (fetch_xs_max hs);;

When I execute the above source code, it occurs the following error message and I found the data type of fetch_xs_max hs is int option, not int.

Error: This expression has type int option
       but an expression was expected of type int

For now, I think I may be apply some tricks likely string_of_int to let Printf work. But, how to do it better?

What integer do you want to print when fetch_xs_max returns None?

So, is it the best way that write a special output function to detect fetch_xs_max whether None or not. Second is convert int option to int ?

Rather, it is a question that must be answered, and OCaml does not try to answer it for you. But here are examples of both of your guesses:

(* for our purposes 0 is sufficiently None *)
let int_of_intoption = function None -> 0 | Some n -> n

let () =
  Printf.printf "%d\n" (int_of_intoption (fetch_xs_max hs))

(* we'd rather do something different on None *)
let () =
  match fetch_xs_max hs with
  | None -> print_endline "There wasn't a max value!"
  | Some max -> Printf.printf "%d\n" max
3 Likes

Either way, you have to define what happens when fetch_xs_max is None. Converting int option to int also needs you to decide what value does None converts to.

Or if you meant that, rather than using option, you opt to use e.g. -1 for None, it can be done, but now it 1) isn’t as explicit, and 2) need to make sure whatever int value you use does not clash with the actual members of the list (what if it’s a list of negative integers?).

With option, the compiler makes sure that you are aware of it and are handling it properly.

4 Likes

Thank you very much ! The examples are very inspirational for me.

Thank you for your advice. If list element are all unsigned integer, I will adopt your good suggestion.

There are a couple of very-helpful common operations on values of type 'a option that would be useful for you here.

The first is a function that takes another function and an option and, if that option is Some x rather than None, applies the function to x. This has a couple of different names, depending on the implementer. I tend to call it “apply” or “apply_if_some” when I use it in my codebases (it’s not in the stdlib), and it looks like this:

let apply_if_some (f : 'a -> unit) = function
    | Some x -> f x
    | None   -> ()

In your case, you could use this function to apply_if_some print_int (fetch_xs_max hs). That said, going this route won’t print anything if your option is None.

The next function that could be useful is one that takes a default value, and fetches either x if the option is Some x, or else your default value if the option is None. It’s also not in the stdlib, and I tend to call it “get_or_default” when I use it:

let get_or_default (default_value: 'a) = function
    | Some x -> x
    | None   -> default_value

In your case, you could also use this function to print_int (get_or_default 0 (fetch_xs_max hs)), but then in the case that the largest value in your array is 0 at some point, you won’t really be able to tell, since 0 is the default “I found nothing” value. The same applies if you use -1, or really any other int value.

So the next function that could be useful is basically “map”, but for 'a options. I’m really surprised this one isn’t in the stdlib, honestly. It looks like this:

let option_map f = function
    | Some x -> Some (f x)
    | None -> None

So then, using most of these functions together, you could do this:

fetch_xs_max hs 
  |> option_map string_of_int (* Convert the int option to a string option *)
  |> get_or_default "None"    (* Get the stringified-int out of the option, or else the string "None" *)
  |> print_endline            (* Print the result *)
3 Likes

The replies here do a great job of conveying basic concepts in handling the option type, and in manually generating printers for things of type 'a option. Those concepts and techniques should be understood and mastered! :slight_smile:

However, lest one come away thinking that lots of boilerplate is required whenever one wants to print a simple value, I wanted to point out that you can use standard pre-processing tools to generate these kinds of functions for you, whenever you don’t need customized and hand-tuned printing.

Here’s how you can print int option values using one of the ubiquitous [@@deriving] plugins, without having to write any boiler plate:

Having first run

opam install ppx_deriving

to ensure that the ppx_deriving package is installed, you can drop in to utop and

utop # #require "ppx_deriving.std";;
utop # print_endline ([%derive.show: int option] (Some 12));;
(Some 12)
- : unit = ()
utop # print_endline ([%derive.show: int option] None);;
None
- : unit = ()

The basic inline syntax to pay attention to here is [%derive.plugin_name: t], which derives a function (t -> type_generated_by_plugin). So, instead of using the derived function when calling print_endline, as above, we can instead obtain a function of type (int option -> string) that we can reuse at will, like so:

utop # let show_int_option = [%derive.show: int option];;
val show_int_option : int option -> string = <fun>
utop # show_int_option (Some 123);;
- : string = "(Some 123)"
7 Likes

Here’s an easy way to print an int option as either Some x or None:

#require "fmt";;

let x = Some 4

let () =
  Fmt.pr "The value is %a@." Fmt.(Dump.option int) x
$ opam install fmt utop
$ utop test.ml
The value is Some 4
4 Likes

There are many useful replies here about how to handle option values. I understand it was not asked for but I still would like to point out that fetch_xs_max, which computes the maximum from a list of values, could be simplified:

let max' = function
  | []    -> None
  | x::xs -> Some (List.fold_left max x xs)
3 Likes