Optional no-value arguments

This is undoubtedly a FAQ many times over, so feel free to point me to discussion elsewhere. “no-value argument” in the title is not a very good term. I wasn’t sure what to call this.

When defining a function, I often feel that it would be convenient to be able to define an optional argument that doesn’t need to be followed by a colon and a value. The mere presence of the optional argument in the function call would be enough to make something different happen. Perhaps such arguments would automatically have the type unit option. For example, if ?? were syntax for defining such flag arguments, I could do this:

let f ??double x =
  match double with
  | None -> x
  | Some () -> 2 * x

Then f 3 would return 3, while f ~double 3 would cause Some () to be passed to the function, which would return 6.

I’m sure I’m not the first person to think of this, but the idea has apparently been rejected. Are there any very significant reasons for that? Maybe it’s just that it’s felt that the cost of adding, say, :() to an argument is so small that it’s not worth changing the language.

(I don’t think that no-value arguments would present any complications for partial application that are not already raised by the existing optional argument system, but maybe I’m missing something.)

Oh–I just realized that the function call syntax would conflict with passing optional arguments that represent their own value, as in this example from RWO:

let ratio ~num ~denom = float num /. float denom;;

let num = 3 in
let denom = 4 in
ratio ~num ~denom;;

This means that a character other than ~ would be needed to pass “no-value” flags. Is there any character available for this purpose? Let’s say that ! is. Then my original second function call example would read f !double 3.

I think that would conflict with supplying an argument resulting from dereferencing some 'a ref.

It seems like it’s more flexible just to use a Boolean parameter, even if that sometimes leads to increased verbosity. As soon as you want the flag to be toggled based on some computation or user input, it seems like you’d end up using a Boolean anyways. In that case the syntax is very lightweight when the variable containing the Boolean has the same name as the function parameter.

Right–I forgot about the use of ! for deref-ing. So some other character would be needed.

I think the unit option is not a bad option. I’ve seen this pattern a lot.

let f ?double x =
  match double with
  | None -> x
  | Some () -> 2 * x

let _ = f ~double:() 5

I often do this:

let f ?double x =
  match double with
  | Some true -> 2 * x
  | _ -> x

let _ = f ~double:true 5

I guess this is what @jyc was referring to. It has the advantages of being able to substitute a computation for true, quickly replace it with false, or being explicit in the false case, when you need that (without having to resort to ?double:None or something). I also think it’s more clear even in the true case.

When true and false are not clear enough, I replace them with a two-case polymorphic variant.

4 Likes

These are helpful responses. I do think that in some cases using :true and :false would be better. For other cases, I feel that having to pass ~double:true or ~double:() seems unnecessarily noisy. I hate having to write ~blahblah:true for functions I’ve defined, when the fact that ~blahblah is there ought to be enough. There’s a tradition in shell programming and many languages of allowing this kind of presence/absence functionality. Not that OCaml does or should follow all other languages, but this seems like a simple and useful addition, to me. I’m not saying that it should be added. I’m just surprised that it hasn’t been added, given all of the other flexibility we have with optional arguments, or, if not, then I would think that there would have been discussion and an explicit decision at some point that it’s a bad idea or a not-good-enough idea.

(Maybe rather than ??double, or whatever the syntax would be, mapping absence automatically to None and presence to Some (), it would be better if absence mapped to false and presence to true. But that’s just hypothetically if there were to be such an feature.)

So I think None and Some () carry the same amount of information as false and true, and the latter is simpler to boot. You can make them optional too, of course:

let f ?(double=false) x = x * if double then 2 else 1

Then,

  • f 1 = 1
  • f ~double:true 1 = 2

And of course,

  • let double = true in f ~double 1 = 2

The last one gets pretty close to your goal of concision, I think.

2 Likes

Thanks @yawaramin. Yes, I think this idea is a good solution for some contexts. It doesn’t achieve what I wanted in general, though. It’s clean, but for a single use of ~double, it’s more verbose than using ~double:true.

All of the suggestions given so far are helpful given the current version of OCaml.

To me ~double:true, or perhaps better, ~double_it:true, is redundant. That is, the Boolean character of the flag can be built in to its name.

~double:() is more succinct, and only uses three unnecessary characters (as I see it), but it’s confusing because passing unit always seems unnecessary (there is only one argument of that type).

Again, I’m not asserting that value-less optional arguments must be added to the language, but I do think they would be nice. Part of the issue for me is just aesthetics, but another part is that I do a lot of experimentation in toplevel, and extra typing there is more annoying, I find.

(Could I define my own syntax for optional flags using camlp4 or ppx or something like that? I don’t know any of these syntax extension systems, I’m not sure I want to learn them just for a small change that is likely to confuse readers of my code anyway because it’s nonstandard.)

(In general I find that when I suggest additions to a language, or ask why it doesn’t include feature X, most responses suggest workarounds given the current language. That’s not specific to OCaml; I have had the same experience in discussions about at least one other language. This kind of response is certainly helpful in the short run. In some cases I’m already aware of the workarounds, but it doesn’t hurt to spell them out for others. However, that sort of response gives me the the feeling that I’m supposed to think that the language in its current form should be considered to be at a local optimum, and should not be changed–but without any discussion of whether it’s correct that it should not be changed. Usually the change I suggest is simply the addition of a new function. In this case, the suggestion is a bit deeper since it would change syntax.)

i think what @yawaramin may have meant is to define a global value let double=true in the library which would allow you to use the from f ~double x anywhere in application code. it’s a bit invasive of the namespace but you would get the full brevity of your desired syntax. in lacaml there are many functions with a signature containing ?(trans=`N), and a transposed matrix is indicated by `T. i then defined let trans=`T to be able to call f ~trans x.

2 Likes

incidentally: how does one write a backtick within code snippets?

Ah, that’s a nice idea, @n4323. It’s only suitable for some situations, but would be convenient when it fit.

You can get `Foo using:

`` `Foo``
1 Like

thx! i have edited my original post.