Confused about the application operator's behavior with type variant constructors

Hello!

Given this simple code:

type side = Side of int
type square = Square of side

let sq =
  let five = Side 5 in
  Square five

let sq2 = Square @@ Side 5

I would like to know why sq2 won’t compile:

8 | let sq2 = Square @@ Side 5
                ^^^^^^
Error: The constructor Square expects 1 argument(s),
       but is applied here to 0 argument(s)

Side is a function that takes an int and returns a side (as show on line #5), so I’m a little puzzled about the current behavior.

I’ve played around with this problem and it seems that most operators overall don’t behave the standard way with type variant constructors.

Could anyone enlighten me?

In brief, variant constructors are not functions (in the sense of OCaml functions).

In particular, if variant constructors were functions

let x = Some (fun x -> x)

will not have the type ('a -> 'a) option due to the value restriction as you can check by writing

let some x = Some x
let y = some (fun x -> x)
let error = Option.map (fun f -> f 0) y , Option.map (fun f -> f "zero") y

There are other difference between constructors and functions, for instance variant constructors has a well-defined notion of arity:

type ('a,'b) p = Pair of 'a * 'b (* this is not a tuple argument *)
let pair (x,y) = Pair (x,y) 
let error x = Pair x

Overall, it is better to not conflate variant constructors and functions in OCaml.

Thank you for your answer.

This notion of well-defined arity for variant constructor looks a little tricky!

For instance, not_ok does not compile here, which I find surprising.

let ok = Option.map (fun x -> x * 2) (Some 1)
let opt_arg = Some 1
let not_ok = Option.map (fun x -> x * 2) opt_arg

Because map expects an 'a option/2 as a 2nd argument (says the LSP server)

Where can I learn more about this behavior? It’s quite different from what I’ve seen from Elm, Haskell and F#.

I’m not totally sure that last one is related to my initial issue though :slight_smile:

IIRC, it’s a decision made long, long ago. SML/NJ allowed the sort of conflation of constructors and functions that you describe, but CAML-Light forbade it. It also instituted the rule that Capitalized names were constructors and uncapitalized names were variables. I don’t remember anymore why, but those are baked pretty deep into the history of OCaml. We’re talking 1990, so … 32 years.

Pretty much predates everything except SML/NJ (Haskell came out around the same time, but was nowhere near usable – IIRC bootstrapping took 3 days at the time).

You should be :slight_smile: I don’t have a working ocamllsp to try, but the compiler will acept not_ok without a peep.

Cheers,
Nicolas

Oh no, major face palm moment regarding problem #2! :man_facepalming:

I’m reading a book and implemented (and overwrote) the option type as an exercise:

Which I then forgot about when I played with the snippets above, and thus got majorly confused!

So basically, I did this:

(* nice confusing bug from me *)
type 'a option = None | Some of 'a
let opt_arg = Some 1
let not_ok = Option.map (fun x -> x * 2) opt_arg

False alarm, sorry for the trouble, the world feels saner all of a sudden :sweat_smile:

This design choice is nicely explained in Section 4.1 (p 35) of the original ZINC paper https://xavierleroy.org/publi/ZINC.pdf.

Cheers,
Nicolas

2 Likes

So I played with your examples again @octachron, the difference is quite subtle. I’ll remember not to think of them as functions altogether as you advised :slight_smile:


That’s great feedback thanks! I was wondering why things were different in Ocaml.

I suppose it’s got something to do with clarity. I remember being very confused when I first learned about opaque types in Elm.

type Dog = Dog

vs the somewhat clearer Ocaml construct : you can’t confuse the type name with the variant.

type dog = Dog

Thanks Nicolas, this looks like an interesting read.

[I’ve done this before, but]
Can I just put out a gigantic recommendation that anybody who has any interest in programming languages and their design and implementation, should read this Xavier’s Master’s thesis (the ZINC paper) ? It’s amazing, and everyone should read it. I learned so much, reading that paper.

1 Like