Hacker News new | ask | show | jobs
by weberc2 2513 days ago
This is confusingly phrased. Do you mean "If Go people were ever shown ... ML"? I would totally drop Go for an ML language if any of them had a sane syntax, usable build tooling (including native, static compilation by default), and a (single) decent standard library.

A super awesome type system is worthless without the basic requirements for scalable software development.

4 comments

If StandardML had even a fraction of the money behind go, there would simply be no contest.

Ocaml is billed as the pragmatic ML, but the syntax really sucks and nominally typed structs aren't nearly as good. They also have 3 competing standard libraries.

Haskell is too ivory tower. Most devs simply can't be bothered.

StandardML is that awesome middle. The language choices are pragmatic compared to haskell (mutable refs, side effects, and not lazy). The syntax is super simple and consistent (unlike ocaml). The concurrent ML extensions offer good multi-threading (still waiting on ocaml). The mlton compiler is very fast. There's only one standard library and it's decent. The big thing holding the language back is third-party libraries. If Google threw their millions at SML instead of go, SML really would be better in every way.

>nominally typed structs aren't nearly as good

OCaml structs are structurally typed.

> They also have 3 competing standard libraries.

There is only one standard library, stdlib.

You know what he meant, if you want functionality comparable to the Go stdlib you are going to have to choose between Core or Batteries, then for async do you choose Async or Lwt? And as said the build system (when you're used to Go) is poor. I like a lot of what OCaml offers, but there's a lot of friction.
> if you want functionality comparable to the Go stdlib you are going to have to choose between Core or Batteries

He is talking about SML here.

Care to elaborate, what Go has to do with Core or Batteries? Both are mostly container libraries, and you don't need them since most of the useful staff is in stdlib.

Check revdeps in OCaml, nobody uses batteries, and nearly nobody safe JS uses Core.

XML parsers, Lwt, servers are in separate packages, which is the only and right thing to do.

>then for async do you choose Async or Lwt

Lwt, it's a non-question. Async is for JaneStreet only.

> And as said the build system (when you're used to Go) is poor.

Dune [1] is far superior to Go's build system. Extremely fast, composable, supporting packaging and os-dependent configurations, extremely easy to config. No abomination like "import github.com/package" or "//go:" in your code.

Go build system doesn't have even a fraction of nice features dune have. For example I could `dune utop ./path/to/my/libs` to build my libs and run a nice repl to test them.

Go doesn't even let you to configure your warnings precisely.

[1] https://dune.build/

> OCaml structs are structurally typed.

You can read about Ocaml's nominal record typing in the [ReasonML docs](https://reasonml.github.io/docs/en/record) (it's more clear there IMO).

SML gets this right in my opinion. If I create a record `{foo = "abc", bar = 123}`, I can pass that record on to ANY function that needs a record that looks like {foo:string, bar:int} fields because it looks at the structure rather than the type of the record constructor.

Another nice property of the SML approach is that you don't need named arguments. Instead, pass a tuple with names (aka a record). One set of syntax rules covers both cases. I also rather like the ability to access record fields with the hash syntax (eg, `#foo myrecord`) when I don't want to destructure.

>You can read about Ocaml's nominal record typing in the [ReasonML docs](https://reasonml.github.io/docs/en/record) (it's more clear there IMO).

You are confusing records with structures, it seems. Structures exist in module language. Records are nominal in SML as well. Access functions for tuples and records are a dirty ugly hack build-in for convenience, they have nothing to do with structural typing, they are inferred in place.

OCaml has structural typed records, they are called objects.

   let obj = object method pi = 3.14; method name = "Pi" end
   val obj : < pi : float ; name : string >
is structurally typed record.

> I can pass that record on to ANY function

No, you can't.

    #foo { foo = 42 }
works, but

    fun f x = #foo x
doesn't. It's an ugly hack, typing is still nominal. A hack, just like SML's arithmetic op overload.

Compare it to OCaml, which have proper structural typing for objects and raw polymorphism

    let f x = x#foo
    f : < foo : 'a; .. > -> 'a
Edit: sorry, indeed typing is structural since you can write `{x:typ} -> typ`.

Anyways OCaml have structural typed records and raw polymorphism and subtyping support for them.

> Another nice property of the SML approach is that you don't need named arguments. Instead, pass a tuple with names (aka a record).

What's your point? You can use records as arguments in OCaml as well.

> I would totally drop Go for an ML language if any of them had a sane syntax, usable build tooling (including native, static compilation by default), and a (single) decent standard library.

F# seems to meet all of that except that native static compilation isn't the default (but is available).

I have found Rust to be a nice middle-ground for this. Sure, the borrow checker takes some getting used to, but it features an ML-style type system (though I still miss some extensions to Haskell's type system available in GHC), rock-solid tooling, and a comprehensive well-documented standard library. The high quality of third-party crates also surprised me. Whether the C-like syntax is sane is debatable though.
Yeah, Rust is the best ML for the things I care about, but after 5 years of on-and-off use, I still haven't adapted to the borrow-checker and a GC is just fine for the applications I write. And learning curve is important too--I need to be able to onboard new developers quickly. Go simply offers the better tradeoffs today for my apps. If someone built a "Rust-lite"--Rust with Go's runtime or Go with Rust's type system (less its lifetimes and borrowing semantics--insofar as those are considered a part of its type system), that would be my primary app dev language. But it's looking like Go is going to get there first with its proposal for generics and hope for sum types.
> a sane syntax

Define sane syntax. C-like abomination? At least unlike C it's unambiguous.

Easily visually scanned and parsed with little mental overhead.
> Easily visually scanned and parsed with little mental overhead.

Sure, I'm asking for actual traits, which makes something easily or hardly parsed. The only languages that I find more readable than OCaml are Ada, Pascal and SML. What causes mental overhead in OCaml/SML syntax for you?

Do you find

     func test (f func(a int, b int) int, x int) int
more readable than

     val test : (int -> int -> int) -> int
Maybe, you've just got used to it?
Yes, I do. Especially when you start considering multiline examples with flow control. There are probably a couple of factors at play:

1. Familiarity. Like (presumably) most programmers, I'm familiar with languages in the C syntax family. Of course you can protest that this is a subjective criteria, but little good that will do you as you try to convince your colleagues to adopt (what they perceive to be) your pointlessly cryptic language for the next project.

2. Visual structure is important and while OCaml's minimalism makes for elegant parser algorithms, it works against human psychology (or so I strongly suspect).

Look, I want to like OCaml/SML. I think the type system is a step in the right direction, but the type system is just gravy and the practical concerns--the fundamentals--are neglected (as much as you may protest to the contrary).

> Yes, I do. Especially when you start considering multiline examples with flow control.

Could you show an example?

> it works against human psychology

What minimalism are you talking about? It uses nearly the same notations as mathematicians used for decades. Most of the constructs are very like to those in Python.