# Is f1()->f2() always a type error?

As a beginner I got stuck on this for a long time while experimenting with external bindings. I though I was misunderstanding external bindings when in fact I was misunderstanding something that looks so simple. (not the binding code below, just a minimal example)

This works

``````let startAt = () => 1
let plus1 = x => x + 1
let end = startAt()->plus
``````

But this results in an error

``````let startAt = () => 1
let plus1 = x => x + 1
let end = startAt()->plus1() // It only accepts 1 argument; here, it's called with more.
``````

What is lurking here? Is something else being passed?

It just doesnâ€™t seem consistent that

``````let f = (x,y) => x+y
1->f(2) // OK
``````

But

``````let f = (x) => x+1
``````
1 Like

Hi @kswope

In ReScript `()` is called unit. Unit would be helpful in functions which accept Optional Labeled Arguments
So

``````startAt()->plus1()
``````

is same as

``````plus1( startAt(), () )
``````

The above code throws an error since plus1 accepts a single argument, but two were being passed to it.

`startAt()->plus` does not throw an error because it is same as `plus( startAt() )`.

4 Likes

Something else that may help this make sense: there are no zero-argument functions in ReScript. `f()` is really just a special syntax for `f(())`, which is a function which takes one `()` unit argument.

When you write `a->f()`, the syntax transform roughly goes like this:

1. `a->f()`
2. `a->f(())`
3. `f(a, ())`

Which brings us to the type error.

6 Likes

I need closure on the original question

Is f1()->f2() always a type error?

I hoping yes. â€ś->â€ť will always take the output of f1(), even if its (), and â€śunshiftâ€ť it into the arguments of f2, including the hidden () of f2. But I canâ€™t help thinking thereâ€™s some deeper rules going on here, or hidden sugar. Like in the Curried functions section here

Itâ€™s possible for it to not be a type error if `f2` accepts two arguments and the second argument is unit `()`.

``````let f1 = () => 1
let f2 = (a, ()) => a + 1
f1()->f2() /* This is the same as f2(f1(), ()) */
``````

But in practice thereâ€™s rarely, if ever, a reason to write functions like that.

Is a () in the definition of a function only allowed because it will terminate optional parameters or is there something bigger here. I just tried and obviously this doesnâ€™t work

``````let f1 = (x, 1) => x
``````

`()` is just the value of the `unit` type, exactly like how `1` is a value of the `int` type. `unit` is special though because it only has a single value, `()`.

The reason why `(x, 1) => ...` doesnâ€™t work is because `1` isnâ€™t exhaustive; there are other `int` values that could be passed to the argument. However, `(x, ()) => ...` works because `()` is exhaustive; there are no other possible values that can be passed to the argument besides `()`.

One thing to note is that the only thing special about `()` is its syntax. Otherwise, it works just like a regular variant. You can replicate its behavior with a custom type:

``````type unit2 = Unit
let f = (x, Unit) => x
``````

Another thing to understand is that function arguments are patterns, just like in the pattern matching that you can do in `switch` statements. This means you can do more complex pattern matching too:

``````type t = A(int) | B(int)
let f = (A(x) | B(x)) => x
f(A(1))
f(B(1))
// This won't compile though:
let f = (A(x)) => x // You forgot to handle a possible case here, for example: B
``````

This is the same reason why destructuring records and tuples works inside function arguments, since destructuring and pattern matching are the same thing in ReScript.

So when you write `(x, ()) => ...` youâ€™re saying "bind the first argument to `x`, and destructure the second argument as `()`".

2 Likes