I'm pretty sure OCaml functions are monadic. They don't really accept tuples (unless you intentionally do something weird).

Rather, if you define (in your mind) a function of three variables, the compiler makes that a function of one variable that returns another function. And that return-value function takes one variable and returns a third function. And that third function takes one variable and returns the result of the triadic function you intended to write.

That's why the type of a notionally-but-not-really triadic function is a -> b -> c -> d and not (a, b, c) -> d. It's a function of one variable (of type a) whose return value is of type b -> c -> d.

A tuple in ML is a product type of ( t * t ...)

OCaML has tuples like all the other MLs. Thus given a function f with a signature (int * int) -> int, invocation is written as something like:

f (1, 2)

So yes, they do accept tuples. This isn't weird, and is the standard way to write a multi-argument function in most of the ML family. In the 5th stage baudrillardian mess that OCaML has become in 2025 it might be weird since at some point they added implicit currying to the language and that became the default idiom. But personally I hate implicit currying and avoid it whenever possible.

It's been a while since I used OCaml or SML, so I went back to check. You're correct that in OCaml functions of multiple parameters are idiomatically represented as curried functions:

https://ocaml.org/docs/values-and-functions#defining-functio...

It's a bit harder to tell the situation in SML, because it isn't really an actively used language. "ML for the Working Programmer" (lol) appears to idiomatically represent them as functions a single tuple parameter:

https://www.cl.cam.ac.uk/~lp15/MLbook/PDF/chapter2.pdf

> SML [...] appears to idiomatically represent them as functions of a single tuple parameter

Tuple is not special, though. Functions accept a single argument of any type.

To use the argument in the function body you can name it, or you can use any valid pattern to destructure it and bind parts of it to local variables.

Tuple is just one of the valid patterns. Coincidentally, it looks like argument list with positional arguments in many other languages. You can also use a record, which makes it look like "keyword arguments". You can also use patterns of custom types.

All the above is still about the single "argument" case, the single value that is "physically" passed to the function. Pattern matching is what makes it possible to bind parts of that value to multiple local variables in the body of the function.

[deleted]