Four Lectures on Standard ML (1989) [pdf]
145 points
2 days ago
| 7 comments
| cs.tufts.edu
| HN
toolslive
2 days ago
[-]
Studying SML should be part of the entry examination for programming language designers. It's old, has its warts, but is still vastly superior to most things that came later. (Type inference? check! Pattern matching? check! TCO? check!. Performance ? excellent!, ...)
reply
cmrdporcupine
2 days ago
[-]
Having SML (or at least OCaml) as a known language definitely would help engineers when they get into the industry and start using Rust.

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.

reply
xyproto
1 day ago
[-]
I also prefer SML over OCaml. I wonder how F# feels and want to give it a shot.
reply
int_19h
1 day ago
[-]
F# doesn't have many of OCaml's more advanced features. In particular, no functors and no polymorphic variants. Also the OO system is basically .NET, which isn't that surprising (but means that you don't get the neat row typing with inference that OCaml does).

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).

reply
sheepscreek
2 days ago
[-]
It’s incredible how much of an understatement it is to say that F# and OCaml were “inspired” by Standard ML. They’re practically step-siblings, sharing more similarities than I could have ever imagined.

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.

reply
int_19h
1 day ago
[-]
For those curious about the differences between Standard ML and OCaml.

http://adam.chlipala.net/mlcomp/

reply
moomin
2 days ago
[-]
C#’s inference is pretty good these days. As with most things with powerful type inference, you can make a real mess if you put your mind to it.
reply
nextos
2 days ago
[-]
The Little Series, famous for The Little Schemer book, published The Little MLer in 1998.

It's probably their lesser known volume, but IMHO it makes a terrific job at teaching the ML family and its features.

reply
ngruhn
2 days ago
[-]
it's crazy, it took "the industry" 30+ years to adopt this stuff
reply
fire_lake
2 days ago
[-]
And they still haven’t! Although expressions over statements would be a breaking change - I can’t see any mainstream language making this switch.
reply
tialaramex
2 days ago
[-]
Barry Revzin has written a paper to try to give C++ expressions like this. It's ugly, even by the standards of C++ but it would work. The rationale is that C++ 29 or C++ 32 wil probably get pattern matching and pattern matching doesn't have great ergonomics if everything is a statement rather than an expression. There are plenty of other things C++ will need to fix once it gets pattern matching, but the direction makes sense if you insist on trying to teach this particular old dog new tricks.

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.

reply
BalinKing
1 day ago
[-]
I actually think it's becoming fairly common these days—IIRC Ruby and Rust both prioritize expressions over statements.
reply
int_19h
1 day ago
[-]
They do, but the fact that there is even a distinction at this point is rather baffling. If your type system has the unit type and the bottom type, all statements should just be expressions having one of those two types.
reply
needlesslygrim
1 day ago
[-]
Well, Rust barely has statements [1]. Nearly everything in Rust is an expression, and AFAIK statements /are/ essentially expressions that yield `()` [2].

[1]: https://doc.rust-lang.org/reference/statements.html

[2]: https://play.rust-lang.org/?version=stable&mode=debug&editio...

reply
steveklabnik
23 hours ago
[-]
> AFAIK statements /are/ essentially expressions that yield `()`

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...

reply
needlesslygrim
11 hours ago
[-]
Oh I see. I assumed being able to explicitly yield unit meant statements were essentially just syntax sugar for unit. Thanks for the correction.
reply
steveklabnik
2 hours ago
[-]
No problem! This really only comes up in very edge cases anyway, which is partially why I don’t think it’s the worst mental model for most people. As long as you know that you can’t put let in expression position, that’s really the only place it goes wrong. Most people who haven’t used a language like Ruby would even think of items as being expressions in the first place.
reply
int_19h
19 hours ago
[-]
Out of curiosity, why does Rust make the expression/statement distinction? Does it interact with the borrow checker somehow?
reply
steveklabnik
17 hours ago
[-]
> Does it interact with the borrow checker somehow?

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.

reply
int_19h
1 hour ago
[-]
> Like, in Ruby, where everything truly is an expression, importing a file and then evaluating it has side effects.

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.

reply
steveklabnik
45 minutes ago
[-]
I guess if you required ; after all of them, sure. Rust doesn’t, so I didn’t think of that.

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.

reply
dragonwriter
1 hour ago
[-]
> Like, in Ruby, where everything truly is an expression, importing a file and then evaluating it has side effects.

That's not so much “everything is an expression” as “everything is at runtime”.

reply
steveklabnik
57 minutes ago
[-]
Eh that’s fair.
reply
fire_lake
13 hours ago
[-]
People really like their early return statements however.
reply
garethrowlands
1 day ago
[-]
Kotlin is also nicely expression-oriented
reply
tmountain
2 days ago
[-]
Still waiting on real pattern matching in TyoeScript.
reply
no_wizard
2 days ago
[-]
You won’t get it till TC39 adopts it
reply
Almondsetat
2 days ago
[-]
do you have any research that points to these things being useful for the industry?
reply
sudahtigabulan
1 day ago
[-]
The languages that were actually adopted by the industry are not exactly full of great useful stuff:

https://www.destroyallsoftware.com/talks/wat

reply
rokkamokka
2 days ago
[-]
This was the language they taught in our functional programming course while I was doing my CS degree. That was around 15 years ago now. I wonder if they still use it in the course
reply
cantrevealname
2 days ago
[-]
FYI for everyone: This is not about Machine Learning. It is about a programming language called Standard ML where ML stands for Meta Language[1].

[1] https://en.wikipedia.org/wiki/Standard_ML

reply
tialaramex
2 days ago
[-]
I was taught the Standard ML of New Jersey (which as a non-American I did not realise is a joke, it's referring to the company now known to you as Exxon, the Standard Oil of New Jersey) at university.

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.

reply
sshine
2 days ago
[-]
Thank you for sharing this American joke.

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.

reply
jjice
2 days ago
[-]
Agreed - we wrote an ML style language in my uni compilers course and it was taught by the primary maintainer of MLTon (Dr Fluet is a great guy). Since it’s full program optimizing, the compilation took a while. He told us to give SML NJ a try for faster compilation but slower execution. It was a headache and only marginally faster compilation for our use case.

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.

reply
layer8
2 days ago
[-]
> Exxon, the Standard Oil of New Jersey

It would have been fun if they had renamed to JSON instead of Exxon.

reply
smlismyhomeboy
2 days ago
[-]
Huh. As an American, I had just assumed it was because it was developed at Princeton.
reply
JoelMcCracken
1 day ago
[-]
Ditto! I've never heard of "Standard Oil of New Jersey" myself.
reply
pkal
2 days ago
[-]
Not everyone; I'm the kind of person who wishes posts about machine learning would be prefixed with these kinds of clarifications ("watch out, this is not related to the programming language but a family of stochastic algorithms referred to as 'machine learning'"), because I consistently fall for it.
reply
belter
2 days ago
[-]
"Generational list of programming languages" - https://en.wikipedia.org/wiki/Generational_list_of_programmi...
reply
msarnoff
2 days ago
[-]
My favorite bit of SML trivia: the infix function composition operator is “o” — lowercase letter o — so you can write ‘(f o g)(x)’ just like mathematical notation.
reply
ckmate-king-2
1 day ago
[-]
Note that these lectures are from 1989, but the definition of Standard ML was revised in 1997. See, e.g., https://www.smlnj.org/sml97.html.
reply
swatson741
2 days ago
[-]
Defiantly worth studying SML imo. Pattern matching is a cool feature. Although it's not as comprehensive as most of the pattern matchers in Lisp. You can't match on bitfields, comparisons other than equality by value, etc.

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.

reply
ossopite
1 day ago
[-]
I suppose by enumerations you mean sum types. I would argue that these are pretty fundamental? you have product types (structs/records/tuples) - a value is made up of x and y - and sum types - a value can be either X or Y. I think the combination of these is what you need to precisely express any concrete data type.
reply
swatson741
1 day ago
[-]
I did mean sum types, variants, etc. It's not really clear what I meant by representing the data but I'm referring to type inference. SML can't solve the problem, and Lisp doesn't have it.
reply
uzername
2 days ago
[-]
We used SML in my undergrad compilers course. I really loved the language and I started exploring other languages outside of class after that.
reply
trollied
2 days ago
[-]
My CS degree course used SML as the first language to teach everyone in the first semester. Put everyone on the same level, as it was unlikely people would have prior experience. Also made it easy to teach things like recursion.

Really enjoyed it.

reply
peterstjohn
2 days ago
[-]
Ha, same here! It really helped my imposter syndrome, as I overheard a couple of guys talking about the ARM assembly they were doing on their Archimedes on the first day…and I hadn't written anything fancier than QuickBASIC at the time…
reply
trollied
1 day ago
[-]
Was actually lucky enough to be taught by the co-inventor of the ARM cpu. Furber is awesome.
reply
peterstjohn
1 day ago
[-]
For my sins, I didn't actually realise how great that was until quite a bit afterwards! ;)
reply