Does anyone use this optional-argument style?

I’ve seen ? used to pass an optional argument on from a function taking it optionally, to another function taking it optionally, e.g. let f ?x = g ?x. Here it’s instead used with optional values constructed in other ways:

let f ?foo () = foo

let special_use () =
  let foo = List.find_opt ((=) 2) [1; 2; 3] in
  f ?foo ()

let ordinary_use () =
  match List.find_opt ((=) 2) [1; 2; 3] with
  | None -> f ()
  | Some foo -> f ~foo ()

Edit: I find it interesting that the OCaml syntax has special support for options. The question is if this is bad style? Maybe the usage of ? when passing the value to the function isn’t explicit enough about what is passed.

I wouldn’t consider it bad style, but I’m hardly an authority on OCaml style. Using ? that way also has a minor performance benefit as it makes it easy for the compiler to avoid unnecessarily examining the option and then allocation another one. Checking with it seems that OCaml is indeed not able to avoid the redundant work when you use match and ~foo instead of ?foo.

1 Like

I think the main use with this notation is to have default values.

Let’s say you want to create a http client. You may have an optional ?port argument. If you give a port number (~port:8080), the function will run with a Some 8080 as port. If you don’t give a port number, the function will have port=None and will probably use the 80 port which is the default port of the HTTP protocol.

Here, the ?port notation for the caller is not useful. It can be if you want to build a new http client with extra functions, with a ?port argument (with the same idea in mind), and forward this argument to the first function as is with the ?port notation.

Lablgtk3 is a typical library with optional parameters (the Window class has plenty of arguments… and we usually don’t need to set each).

I would say this is a rather common way of threading optional arguments through functions calls. In fact, if I was doing code review, I would immediately suggest rewriting ordinary_use as special_use, which makes it clear that you are passing the optional argument unmodified.