Hacker Newsnew | past | comments | ask | show | jobs | submit | yallop's commentslogin

   > Things I Disliked
   > [...]
   > • using ; heavily as element separator (e.g. in lists and records):
   > 
   > let some_list = [1; 2; 3; 4; 5]
   > 
   > You get used to this, but it’s a pretty big departure from the norm to use commas. I wonder what’s the reasoning behind this.
It appears to be a result of the limitations of early parser technology, as Dave MacQueen explains:

    "One of the kind of interesting things is that the parser was written based on Vaughan Pratt's precedence parser [...] and it had the peculiar property that it was hard to reuse symbols for multiple syntactic purposes, and hence you get things like comma used for pairing that means you have to use semicolon for list separator, that means you have to use semicolon-semicolon for statement separator, so there's some peculiarities based on the fact that we didn't really know how to parse and we accepted this quick hack from Vaughan Pratt"
    (https://www.youtube.com/watch?v=ua3EYopCURo&t=179s)


The article briefly mentions ReasonML, which offers a different syntax which aims to be more familiar to JavaScript programmers but is also likely to be more familiar to most programmers of C-family languages.

There’s also ReScript, which is a very similar alternate syntax which sort of split off from the ReasonML community in a confusing and complicated sequence of events which is frustrating to try to follow for someone who was just interested in the programming language and tooling: https://rescript-lang.org/blog/bucklescript-is-rebranding


> The article briefly mentions ReasonML, which offers a different syntax which aims to be more familiar to JavaScript programmers but is also likely to be more familiar to most programmers of C-family languages.

One thing I don't like about this is that they've replaced:

> let x = 1 in

with

> let x = 1;

In plain OCaml you only use semicolons on lines that evaluate to unit, so they're a clear indicator that you've done something with side effects. ReasonML loses this useful visual distinction in order to look more like JavaScript.


Agree. ReasonML threw the baby out with the bathwater. If ever there's a language to not immatate it's Javascript.

Would be interesting to see a something that updated some ocaml quirks, but stayed true to the spirit of the syntax. Ie comments and multiply but not ;


That split was really bad. At the height if its popularity (yes, there was a brief moment in the sun) it was also influencing the OCaml community IIRC, bringing improvements like friendly error messages and syntax improvements.

Now though... Who cares about either of the Re* languages?


ReScript community is well and alive and is actively working on improving the ReScript compiler and tooling. V10 just came out with records with optional fields - https://forum.rescript-lang.org/t/ann-rescript-v10-released/.... Async/await is in the works. The language is really really good (especially say comparing to TypeScript). Make sure to check it out.


ReScript is amazing. I really don't know much about the split (it's not like I didn't try to understand it, I just can't understand the anger of non-Ocaml developers at the split).

Rescript allows me to write Javascript in a sound type system, while remaining very close to how you would write code in Javascript.

Rescript to me feels like Typescript + a very prescriptive linting system which prevents a lot of problems, and since the linting system wouldn't let you do certain things, might as well get rid of them from the Type checker, this results in a much faster compiler.


I have been using ReScript for (almost) a year as a production programming language. There were a few limitations and confusions at first, (and I don't think it will be replacing TypeScript in any near future) but I can't even think about going back to a work environment uses TypeScript, the dev experience is so good :)


I think ReScript community is pretty active right now, I have big expectations about future 10.x releases (I'm working for a company uses ReScript as a main programming language) there are less buzz recently though.


No, it’s actually because , (comma) denotes a tuple.

    [1, 2; 3, 4]
is a list of tuples, of type (int * int) list, equivalent to

    [(1, 2); (3, 4)]


I never understood the motivation for allowing tuples without surrounding parens. There are a few very simple functions that look cuter this way, but it constantly trips me up in more complex situations.


No parentheses can be nice for cleaner pattern matching, e.g. something like this:

  match a, b with
  | 1, 2 -> x
  | _ -> y


This phenomenon is actually another example of the phenomenon he mentions. In strict precedence parsing, operators are assigned meaning rather than productions of a grammar (which would could those operators in distinct contexts), so once you've decided a comma indicates a tuple, it's easier to just allow this syntax.


The "comma makes a tuple" behavior is a big gotcha I've experienced in Python. I wish that behavior didn't exist


Which version of that are you talking about in Python? The biggest related gotcha I've seen in Python is when you have a trailing comma `x = 1,` and accidentally create a tuple.


Yea that’s the most common gotcha


My biggest frustration with OCaml is having to deal with all of that cruft. I've learned many languages over the years, and I think I've just reached the point where I've lost patience with having to deal with what I would consider substandard syntactic elegance.


This sort of thing is why Rust's Editions are great and why Vittorio Romeo's Epochs (P1881) for C++ would have been the right choice.

I like Editions because of the cultural consequences, Rust's community assumes they can fix things and so they set out with that goal, even in cases where Editions won't quite do it - while the C++ community tends to accept the state of the language as a static fact and just "take it". But this sort of thing isn't about the cultural effects, it's a direct technical achievement.

If OCaml had Editions, it could say OK, that syntax was a bit rubbish, here's 2023 syntax which fixes two things everybody hated, get back to us over the next few years and we'll decide if there are further changes needed. Without losing all existing OCaml software or demanding expensive rewrites.

Even better, changes of the sort in Editions are mechanical. It can take a bit of creative work to do it nicely but the transformations can be automated, so people who have "old OCaml" and wish they had "new OCaml" can push a button and get on with their day. Having both this and the feature which keeps old code working unchanged, add up to an experience where the community can move forward, on syntax at least, and not be trapped with yesterday's mistakes.


The syntax is a little unorthodox, but just having a literal syntax _at all_ for things like lists/arrays beats having to call `new` on some random class you have to import and then doing a bunch of in place updates to add all the data.


In this respect C# has knocked it out of the park with shorthand initialisation syntax for arrays and objects. But it's newer and could look at what other languages are doing/not doing in that regard.


I don't mind the cruft. I think a bit of harmless quirks can give a language character.

It actually makes the learning easier. Now that I know the story why, I have memorized how elements are seperated in OCaml for probably the rest of my life. I just need to remember the story.

Plus not reusing symbols for multiple syntactic purposes actually fits nicely into the general Ocaml philosophy. Plus is makes my complexity-hating, minimalist heart happy.

Though I am at that point in my life where I don't really care about syntax that much to begin with. Yes syntactic complexity matters. How fast it can be parsed, how easy macros are implemented, these things can matter but the actual syntax: boring. White-space sensitive or not, curly braces or begin/end and all that stuff is so dull. I am happy with whatever.


If using semicolons instead of commas is enough of a showstopper for you it's probably that you just don't have a need for the language :).


Honestly, I'd argue that few people do. At a previous employer, we used it in situations where would have been much better off with a more mainstream language, in my opinion.

Of course, there are few situations that require any particular language.


Of course. But for example if you are writing a compiler, an interpreter, or anything that has to manipulate something that resembles a structured tree of meaningful data, OCaml is particularly well suited :).


Any language with ADTs and pattern matching is reasonably suited for that task, and unlike 20 years ago, these aren't obscure features anymore, so there are many options.


Yes but OCaml syntax is Pascal inspired. It’s always going to be a lot more elegant that anything inspired by the horror that is the B syntax. It’s just less familiar to people who grew up using C, Java or JavaScript.


One man's 'cruft' and 'substandard syntactic elegance' is another man's 'decades of backwards-compatibility preserved'.

People are of course free to use the newest languages with the shiniest syntax (and many do), it's just that they pay the price by using untested, unproven languages when they could have been using ones which have been battle-tested in production for literally decades.


what's your stance on the curly braces (which is also a consequence of early parser technology) ?


What are some good alternatives? I don't find begin and end as easy to spot at a glance if the indentation is off. I'm not sure why I don't like indentation based, but I don't in practice. A good IDE helps me care less, and I don't think I could write python well without a good IDE anyway.


I think they're often helpful for reading, but I admire what Scala did in Scala 3 to eliminate their necessity.


Scala 3 actually doesn't really eliminate braces in any meaningful way. Sure, they're optional for delimiting traits, classes, and objects. But braces are heavily used for block scoping in Scala too. E.g. a very common pattern:

    retry(Seq(1.second, 2.second, 3.second)) {
      // some action that can fail
    }
This still requires the braces as of right now. I think the syntax changes will just confuse more people than it helps, at least for the time being.


That is a curried parameter in {}. Those braces are not for scoping. They basically define a lambda. You can probably use () instead

you can also use import language.experimental.fewerBraces with nightly builds to write code like this

List(1,2,3).map: c => ...


The braces here absolutely denote a new scope. You can't bind vals inside parentheses, for example. You can inside braces.

> You can probably use () instead

I would rather not as using braces for the final argument has been the Scala idiom for a long time now.


I see your point. However you don’t have to define that thunk inline. You can write it separately and just pass it by name retry(…)(thunk). Not sure what signature of the retry method looks like.


You can define it separately but it's often not the idiom. Especially for call-by-name arguments like in this case:

    def retry[A](backoffSeconds: Seq[Int])(action: => A): A
In this case you can't just define a 'val action = ...' because you don't want it to be run immediately, you want to run it inside the 'retry' method only. You could define it as a 'def' but then you're just introducing two different delayed evaluation concepts into this small piece of code. The cleanest way is to just directly pass in the action and Scala ensures it is delayed evaluation thanks to the CBN parameter type:

   retry(Seq(1, 2, 3)) {
     println("Trying")
   }


Documentation for 'cached' is on its way. There'll be a blog post up soon with a friendly introduction, and some representative benchmarks. In the meantime there's a drier but more detailed specification of the behaviour of 'cached' waiting in a pull request here:

  https://github.com/yallop/docker.github.io/blob/d9a57867/docker-for-mac/osxfs-caching.md


There are some difficulties with moving arbitrary values between phases: it's easy to move an int or a list, but what about a mutable reference or a closure?

However, I don't think this will ultimately be a problem in practice, for two reasons. First, global values can be used in different phases via "module lifting". Second, there's a separate proposal for adding overloading to OCaml in the form of modular implicits:

https://www.cl.cam.ac.uk/~jdy22/papers/modular-implicits.pdf

Modular implicits will make it possible to use a single overload function ('lift', say) in place of a family of functions 'Expr.of_int', 'Expr.of_float', etc., which will make things much less awkward. And it's only a small step from there to having 'lift' called implicitly/automatically at appropriate points. Here's a message from an earlier discussion with a few more details:

https://sympa.inria.fr/sympa/arc/caml-list/2015-05/msg00032....


> There are some difficulties with moving arbitrary values between phases: it's easy to move an int or a list, but what about a mutable reference or a closure?

Wasn't this already answered by "Closing the Stage" [1]?

[1] http://lambda-the-ultimate.org/node/2575


"Closing the Stage" is about a different interaction between staging and closures: that (with some care) staging constructs can be elaborated into closures in an unstaged calculus.

The problem with moving arbitrary values between phases with macros is that values can refer to bits of the runtime environment that cease to exist when compilation is finished.


> that (with some care) staging constructs can be elaborated into closures in an unstaged calculus.

Exactly, which seems to provide the necessary semantics for references that you mentioned. Clarifying staging semantics for difficult abstractions like refs by elaboration into well understood closure semantics was the point of the paper.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: