Back in the day when I played with these things I preferred SML/NJ over OCaml, but OCaml "won" the battle for headspace.
I'd consider using OCaml for new projects, I really like the language family.
OTOH some of the choices it makes are a bit more pragmatic - e.g. arithmetic is overloaded for ints and floats, locals can be marked as mutable similar to record fields, and fields are scoped to record types (so different types can use the same field name).
Edit: Obviously, F# is the step-sibling here, given its half-dotnet parentage. However, they’re all solid choices. Their spectacular type inference makes coding in them a very gratifying experience. I can only think of TypeScript as the closest to them among the more popular modern languages.
It's probably their lesser known volume, but IMHO it makes a terrific job at teaching the ML family and its features.
Also, I think Rust would consider itself to be a mainstream language, though they aren't techically "making this switch" because the language has always been expresion oriented from the outset. The Book has about two paragraphs about the statements in the language and then a whole chapter on expressions because almost everything is an expression.
[1]: https://doc.rust-lang.org/reference/statements.html
[2]: https://play.rust-lang.org/?version=stable&mode=debug&editio...
This isn't true. The inverse is true, "expression statements" can turn an expression into a statement.
What you're seeing in the playground is just how blocks are defined[1]:
> The syntax for a block is {, then any inner attributes, then any number of statements, then an optional expression, called the final operand, and finally a }.
In this case, you have one statement, and no optional expression.
And so:
> The type of a block is the type of the final operand, or () if the final operand is omitted.
So that's how this works.
Now, that being said, I don't think it's too terrible of a mental model to think of this situation in that way. But if we're getting into nitty-gritty details, that's not actually how it works.
1. https://doc.rust-lang.org/stable/reference/expressions/block...
Nope. The borrow checker cares about the control flow graph, expression vs statement doesn't matter.
> why does Rust make the expression/statement distinction?
I am not 100% sure. If I had to guess, older Rust was much less expression oriented, and then, over time, got moreso.
But also, I think it kinda just makes sense in general for the kind of language Rust is. Like, in Ruby, where everything truly is an expression, importing a file and then evaluating it has side effects. Whereas in Rust, 95% of statements are declarations, and of those 95%, the only ones you really use in a normal execution context are let statements. The rest are stuff like "declaring functions" and "declaring structs" and those don't really get evaluated in a language like Rust.
let being a statement is nice because it means it can only happen at the "top level" of a block, and not say, inside a loop condition.
In the context of ML, I think it's a more useful baseline. So declarations are still declarations, but e.g. ; is just a sequential evaluation operator.
> let being a statement is nice because it means it can only happen at the "top level" of a block, and not say, inside a loop condition.
I would argue that it's actually a downside - it means that a loop condition cannot have common subexpressions (that nevertheless need to be evaluated on every iteration) factored out.
I’ve always found code that does this to be more complicated to read and understand than rewriting it in a different way. YMMV, of course.
That's not so much “everything is an expression” as “everything is at runtime”.
I strongly believe that - although my home institution no longer teaches an ML as first language - this is the best way to teach CS to undergraduates. An ML has all the fundamental ideas you will need to also teach about this discipline, and (so long as you choose e.g. SML/NJ or similar, not Rust or something) it won't be a language the average teenager you recruited might already know, so the week 1 exercise showing they've understood what they're doing is actually work for all of your students, averting a scenario where some of them drift away only to realise at exam time that they haven't learned a thing.
Standard ML was my go-to language for many years.
I always found SML/NJ complicated both to compile and use, compared to...
...well, pretty much every other compiler: Moscow ML, Poly/ML, MLton, MLKit.
That said, it’s the OG so I give it some slack. I did enjoy MLton though, but it’s easier to do when the instructor wrote it.
It would have been fun if they had renamed to JSON instead of Exxon.
Datatypes are just ok. Classes would be better. It's sort of strange to represent lists (and everything else) as enumerations. It's not really essential or fundamental but I guess that's what Lisp is for.
Really enjoyed it.