4 ms·
PL theoretic superiority was not the design goal. Golang was intended to be a pragmatic, bread-and-butter language for large teams with churn. Bad code needs to
by zelly 7y ago
PL theoretic superiority was not the design goal. Golang was intended to be a pragmatic, bread-and-butter language for large teams with churn. Bad code needs to be screamingly obvious. It also needs to be very easy to learn. FP langs can never fill this role because it's fundamentally more difficult to think in FP terms than imperative terms. Maybe for small internal projects. (But if it's successful, it'll get rewritten in Go/Java/C++; people simply can't handle FP.)
- cultus 7y agoFP is easier to reason about because humans have a hard time with implicit state. Just because you don't know it doesn't mean its hard. That's why functional-ish things like Redux are taking off, and every new language except Go has incorporated significant functional features.
- zelly 7y ago> Just because you don't know it doesn't mean its hard I spent a long time learning Haskell and have written enough Clojure especially to safely call myself an expert in it. I obsess over languages (not proud of that btw). FP is hard. It's too hard for most programmers. A lot of people avoid saying this but it's true. It's hard in a way that you can't fix with experience. That's why the tooling for it sucks. Suppose you want to make an IDE or even an API for your product. Do you want a lot of people to use it? Then you use ${BLUB}. Heavily abstract stuff dies. Look at Tensorflow vs. Pytorch. Tensorflow 1.0 had this wonderful functional, declarative, immutable way of writing neural networks, but people protested because they couldn't mutate variables in a for loop and have it automagically autodifferentiate. Abstraction kills network effects. > every new language except Go has incorporated significant functional features None of them come close to the semantics of FP (except Scala, but its negative adoption rate kind of proves my point). They all allow side effects which breaks the first rule. You can say they stole some of the convenient syntactic sugar from FP, like pattern matching and lambdas (e.g., Kotlin and Rust). Great. So what advantage do FP langs have now? Making it hard to do I/O? Category theory? Having unmaintained compilers?
- hajile 7y agoHaskell was designed for theory. SML was designed to combine being pragmatic and easy to teach (not only the language, but how to implement). Other than syntax, SML's fundamental nature is much closer to Rust (with a GC and simpler syntax and without lifetimes, macros, or traits). In any case, let's look at those haskell "hard parts" and contrast them. Haskell treats everything as immutable. SML is NOT always immutable. It simply adds some (very sane) constraints on where mutation can occur which makes reasoning about mutation easier. Haskell forbids side effects. This is an illusion of course (you'll find high-performance libraries often use the unsafe IO stuff). SML embraces side effects though general practice is to restrict them to specific modules to make the program easier to reason about (a good practice in pretty much all languages). You can definitely write imperative pieces of code if it improves performance. Haskell is lazily evaluated. This can be a performance boon for some things, but is a bane to find and fixing performance bottlenecks. SML is eagerly evaluated like most other languages. Haskell is crazy for typeclasses. They use them everywhere, but they often complicate actually using things. SML does not have type classes, but SucessorML (the next version) is looking at adding module typeclasses instead. These will have most of the same features, but because they are in the module instead, it should put a big limit on their abuse and make their use easier to track. SML's big "ivory tower" ideas are: powerful type system, pattern matching, option types everywhere, first-class functions, and modules (essentially as a replacement for classes though that is not entirely true). SML really is the whole package. The best parts of functional programming combined with the best parts of mainstream languages (restricted to the "good" subset) all tied up in a language definition that manages to reduce the amount of syntax compared to something like Java or Go while increasing the amount of power while at the same time not turning into an unreadable mess (it's pretty hard to write unreadable SML code).
- zelly 7y agoI assumed Standard ML was more like Haskell than it is. Going through OP's tour, I notice that it has enough features to actually be usable. It has primitives for concurrency and OS APIs.
- eatonphil 7y agoIt's even easier to use if you go with Poly/ML's pthread support. CML is relatively more complex and less supported from what I can tell.
- saityi 7y ago'A Tour of Go' inspired this. The format and some of the material come from there. I think it ended up shorter than the Go tour, and I was hoping it would be very easy to learn Standard ML from it, as I do consider it to be very easy to learn. You can pull up the CML examples next to the Go ones, and other than syntax, they should be the same: https://tour.golang.org/concurrency/1 https://tour.golang.org/concurrency/1 https://saityi.github.io/sml-tour/tour/03-01-spawn.html https://saityi.github.io/sml-tour/tour/03-01-spawn.html There are differences, but once you get to that point in either tour, the differences shouldn't make it hard to read; if it is hard to read or understand, that would be welcome feedback, as I have failed in my attempt to present Standard ML well. :) (I originally posted this as a 'Show HN', hoping for feedback, so it is totally welcome.)
- neilparikh 7y ago> Bad code needs to be screamingly obvious. I'm not sure I understand why this is true for Go rather than a statically typed FP language. If anything, I'd consider languages like Haskell or ML to be better at this. - The obvious case is handling `nil`. In Haskell, you'd treat this as `Maybe a`, and it's obvious that you're not handling the nil case since the pattern match is incomplete. In Go, you get a nil pointer exception at runtime. You could check for the nil case for every pointer, but that's too onerous, so now you're left with relying on the programmer on checking - Go doesn't have exceptions, which removes some of the hiding of bad code (see https://devblogs.microsoft.com/oldnewthing/20050114-00/?p=36693 https://devblogs.microsoft.com/oldnewthing/20050114-00/?p=36...), but you can still accidentally use a result when an error is return (in fact, you have to return something!). In Haskell, you're forced to pattern match on an `Either`, which again exposes bad through an incomplete pattern match. I don't think these are arguments for FP per se, but I do think that statically typed FP languages (and the languages they inspire) are much better about removing footguns and making bad code hard to write, simply because the typechecker is so restrictive. As a more general principle, instead of making bad code screamingly obvious, make it impossible to write! Now, you mention later in the thread that you've already used Haskell before, so you're probably aware of all this and I may misinterpreted what you meant. Did you mean a different form of bad code?