Reason - general function syntax discussion



Hey everyone,

(continued discussion from #1299)

Context: Reason would like to appeal to a broader audience (which, so far, has been JS programmers). The simple approach that’s been taken is to mimic JS syntax in some general sense while keeping mostly the same syntax as ocaml regarding function application/declaration.

Therefore debate is: current syntax vs new JS-esque syntax.

I don’t think I could do justice to either side of the debate so I’d rather let everyone express how they see things here.

Custom syntax sets

It’s sad that some people decided to spent their time and efforts on purely cosmetic changes (just for the sake of syntax to be familiar to javascript developers) instead of actually improving OCaml ecosystem.


I fall squarely in the “spaces are better than parens for function application” camp, for two reasons:

  1. use tuples a lot in my project, so not only will I hit the worst-case scenario f((x,y)) quite a bit it’ll also be fairly confusing for a while after the change
  2. I believe the space syntax lends itself better to partial application, which I find to be a solid benefit of ML style languages and I use it all the time. With brackets function application has a defined start and end point, I’m not aware of any languages that have brackets and partial application. I really hate syntax like f(x)(y) which I’ve seen some JS developers do. This is somewhat backed up by let-def’s statement that the intention is to move away from curried application.

Now, having said all that, I’m in favour of this change. There are enough other improvements here - single colons for named arguments, removing fun for ES6 style anonymous functions - that if parenthesis for function application is the cost of having them I’m willing to put up with it.

Anecdotally I’ve seen a few JS developers comment that while Reason is similar, it’s not similar enough. I’m still a bit worried that long term this change will make things harder than they need to be for use cases like mine but overall I think this is a good direction.


As much as logically it makes no sense, syntax matters to a significant portion of developers (particularly JS developers - just look at the crazy things they’ve done to keep all of their code in nodejs).

I also think Reason solves some weird things about ocaml syntax. There’s no more let vs val, or struct vs sig, everything is a lot more consistent and easier to learn. It’s a bit out of date but here’s the original OCaml/Reason comparison:


@spyder We don’t need to think of this change as having a long term negative impact!
More optimistically, long term I think we’d like to see other syntaxes emerge with the same AST underneath, allowing for big groups to form (C-style, ML-style, lisp-style etc…) without needing things to be re-written each time.

That said, it is still only a dream :wink:


@bsansouci I didn’t mean it would be long term negative for everyone, but I suspect it will make my tuple-heavy project weird in a few places and discourage partial application which is a useful abstraction to have in the toolkit.

Multiple syntaxes is a curious goal. As much as I’d like to use ML style I think there are good reasons not to. We definitely want to see if Reason/JS syntax has legs before we even think about that :wink:


Please keep the discussion thread constructive. OCaml is liberally licensed precisely so that projects like Reason can exist, and our community has a rich history of alternative syntaxes through 15 years of camlp4/5.


In my understanding Facebook had convincing experiences with type inference and statically typed systems – lot of OCaml (pfff, coccinelle e.g.) – and they want to bring the corresponding industrial advantages to their web developers and worked on Hack (PHP) and are now experimenting with Reason (JavaScript). I think the reasoning is similar to the one that founded the development of NodeJS.

That said I don’t understand what kind of debate we should have there? Is the implicit question if we all should move to JS-esque syntax?

As far as I understand, ReasonML is a different syntax, so that derived bytecode or native code don’t “remember” in any way the syntax they were written with. So it just means more libraries and more projects for every one. Now comes the question of contributing and I don’t think the JS-esque syntax would take more than a couple of days to any fluent OCaml-ler to get comfortable with it, if this is required.

That said, I’m not sure the premise of providing a JS-esque syntax to OCaml to easily draw JS-coders to use OCaml is reasonable (pun inside™): OCaml and JS are quite different languages and while I only have layman knowledge in CS I’ve read enough on the OCaml mailing list to understand that requiring the language to make type inference possible has very subtle consequences on what can be expressed and what not, which in tuns leads to a language featuring a variety of concepts that any OCaml programmer will have to learn.


This thread really isn’t meant for OCaml programmers. ReasonML is a thing. It exists, it’s real, and it’s growing, so the concept is already proven. I myself don’t like Reason’s syntax because I’ve already learned OCaml’s. But I appreciate the great effort of the Reason community and their work benefits OCaml.

If these kind of threads will be here, we really need a ReasonML category, and maybe also a post explaining what Reason is.


Huh? Move away from partial application? Is this controversial idea being discussed anywhere in the OCaml world as well? I sure hope not.


let-def is one of the ReasonML contributors, not someone in OCaml. See this specific comment:


@bluddy I think it’s in the context of Reason itself and not OCaml in generally, don’t worry :stuck_out_tongue:

I couldn’t speak for let-def’s arguments, but in my experience, auto-curry has caused more harm than good. I’ve accidentally curried things again and again, forgetting an argument or just having only named arguments, and been very confused for a bit.
The times when I was happy that auto-currying existed are nearly 0. The classic example is (+ 1) [1 2 3] but that case happens very very rarely for me.

I think currying is super awesome, I use it every once in a while, but auto-currying doesn’t seem worth the pitfalls. I wish we could step away from auto-currying and have an explicit way to curry things.


I see. Now that you mention it, I do remember struggling with it quite a bit early on in both OCaml and Haskell. Nowadays I can just tell automatically from the type errors and I don’t even notice it as an issue, but I can see where people new to the ecosystem would have a problem. let-def is pretty active in the OCaml world, which is why I was afraid he was reflecting some growing consensus among OCaml devs. But if it’s just making it harder to curry in the Reason layer, I certainly don’t have a problem with it.


I wouldn’t call it a consensus, but from casual conversations with other members of the OCaml team there are a few, including myself, who agree that if you were starting from scratch it would better to avoid currying and instead have a short syntax for partial application. But OCaml isn’t starting from scratch and currying is so fundamental that you really can’t change it now. Note that this isn’t what Reason is doing. They are trying to use syntax to hide currying and, in my opinion, that is likely to cause more confusion than it saves.


IMHO, refmt should make all syntax discussions moot. People should be able to freely move back and forth across whatever syntaxes they like. Of course, the tool isn’t perfect yet, especially when outputting ‘classic’ OCaml syntax. So perhaps that needs to be the next priority, after this new syntax lands, so that everyone can just have their favourite syntax.


Considering the conversation here, what are people’s thoughts on single-arg functions with currying everywhere (like OCaml), or multi-arg versions with on-call currying syntax’s (say something like &someCall(&1, 42, &2) or some other syntax)?


Are you suggesting this due to performance issues or usability issues, such as newcomers getting confused?


There is no performance penalty when function call is fully saturated. And when it’s applied only partially, there is no more overhead than in any other form of partial application.
I’m also curious what’s the reasoning for avoiding curried functions. It can lead to very concise code when function argument order is carefully choosen (which is not the case, for example, with Jane Street libraries, where “data type” argument is always placed as first argument).


I’m a huge fan of the parenthesized syntax.

I come from a C/C++/C#/Python/Ruby/JavaScript background, and I am simply comfortable recognizing function calls in the form of fn(arg1, arg2)-esque syntax. Ruby also has fn arg1, arg2 at the statement level. It would be familiar for me.

On a more actionable level, I want to start using Reason at my work, and I can’t do that unless I convince everyone on the development team that it’s a good idea - at the minimum, they’re going to be doing my peer reviews, so they need to understand it as easily and simply as possible, and familiarity can be a big help to multi-language developers.

One recognition issue I have with the current syntax is that if I see f g h i, if you accidentally skip the first word or are glancing across the screen and see this from right-to-left, you have to reprocess which function is the root one being applied to. This conundrum doesn’t occur with f(g, h, i) because the function callee and arguments are so syntactically separated.


I’m quoting @let-def from discord:

  1. There is no isomorphism between (A*B)=>C and A=>B=>C in OCaml (where * is product and => is effectful arrow). This leads to trick such as
    OCaml designers also agree that it was a mistake to not distinguish syntactically between partial application and “normal application”. That’s why for instance there is no partial application at all with value constructors.
    So what you consider a core feature is disapproved by language author and Jane Street.
    Partial applications is inefficient to compile and has a tricky semantics (because of effects). It is so tricky to compile that the compiler actually approximates it: when using labelled arguments, the compiler takes some freedom with the semantics to avoid a combinatorial explosion.
  2. The PR doesn’t remove anything at all, it just put lipsticks on this design (or mistake depending on your point of view).
  3. However my message showing possible semantics changes introduces real differences. But that’s not what is going to happen. Instead, what is likely to happen is algebraic effects (with multicore).
    This version will distinguish effectful and pure arrows at the type level, so the new syntax will become more relevant at that point (with the necessary plumbing in the frontend, it could help catching actual bugs).

Which is basically what he said on github