Is there ever a time when it makes sense to choose Go over OCaml?

In languages that lack algebraic datatypes, for each recursive function over such a datatype that we would write in OCaml, one must spread methods across all the subclasses that implement the type. That, or use downcast. Since the former is tedious, at least in my experience with Golang, people chose the latter. Perhaps things have gotten better: does Golang have algebraic datatypes today?

There’s also the importance of immutable data in ML-like (and Rust) languages. Most other languages simply have no equivalent.

1 Like

yes, god forbid. (filler to reach 20 chars)

3 Likes

I think you also need Musl to be completely static with Go, as far as I understand it. Go depends on libc, and you can’t statically link against glibc because it is dynamically linked internally.

The thing that Go’s runtime has that makes the binaries a little extra chubby is the fancy scheduler used to implement goroutines, I reckon.

edit: after looking a bit more into this, it looks like Go only depends on libc for a few things in some libraries, not anything in the runtime itself. That’s nifty.

1 Like

I’ve worked extensively with both and I would say that Golang is much much easier to pick up and maintain. The tooling to start building, formatting, managing deps, linting, etc. is top notch, and the standard library as well as the documentation is state of the art. The language is simple so I’ve rarely seen it being abused and it’s really easy to get into a codebase (they all look the same!)

On the other hand it’s not expressive enough for a lot of usecases (doesn’t even have enums!) and has some security issues by design (nil).

I would say it you’re building a serious project that will need to be maintained over time and needs to be audited: then Golang. If you’re trying to write something complicated and know how to write clean code and not abuse a language’s extended features then OCaml.

2 Likes

I agree with basically everything you said, I’d just add that Go is actually not that simple. E.g. yesterday I had to deal with implementing a mutable queue. I tried basically every combination of storing a pointer to a slice, passing a pointer to the queue struct, having method implementations receive a pointer to the queue, before I got it to work. By contrast OCaml’s ref or mutable record field would have worked instantly. And OCaml is not supposed to be better at imperative programming than Go!

2 Likes

I feel compelled to add my own little story of Golang idiocy. Circa 2015, a Golang interface with (let us say) 2 struct-types that implemented it, enjoyed three different null values: one for the interface type, and one for each pointer-to-struct for each of the struct-types. All three of these nulls were incomparable, of course.

A congeries of special-cases masquerading as a coherent language design.

3 Likes

It “feels simple” as long as you don’t build complex types; once you start down that road, the manifest inadequacy of the type system beats you senseless every day, every hour.

3 Likes

What advantage do you see here vs the existing runtime?

Should provide leaner executables thanks to better dce, which are fully static without the need to be on a musl system, offer better abstractions compared to C and the ability to do more things at compile-time (you can even emulate modules and functors, you have access to coroutines, etc…), offer more control over allocation and arch-specific details (e.g. callconv), all of the above would probably result in writing less assembly.
Most importantly, zig is very good at cross-compilation, and I feel like the biggest obstacle to OCaml having good cross-compilation is its runtime.

We are completely on a tangent now, but does Zig have any success stories? I think it sounds great in theory, but this disclaimer on the website about “we’re a small project and can’t be bothered to make good docs yet” is always a sign to me that this is not ready to be used and may possibly just be some guy’s hobby project.

I don’t think so. The OCaml runtime is written in C, so as long as you define the right macros it’s completely fine with respect to cross-compilation. The issues I’m aware of fall into three categories: build system, library lookup, and lack of host/target separation in the native backend.

3 Likes

Regarding library lookup and build system, I’ve finally gotten a decent grasp of using dune and opam for basic things like building multiple packages, linking them, understanding how to write tests, how to write a library, how to set a lib up for opam, dune project defns.

I forgot how long it took me to get to a comfortable level of familiarity. But now that I’m here, it’s also hard to pin point exactly why it doesn’t feel quite as nice as the npm or cargo cli. Could be because these two cli’s handle both package dep and build.

If there isn’t a good comparison chart of commands between npm/dune+opam, I should make one.

3 Likes

I can’t speak for them, I’m just an outside observer with occasional experience with the language, but there’s already a foundation and a large team and the toolchain is used by big names like uber as a c compiler, there’s definitely something to it.
Otherwise, yeah, the language itself is still evolving. Not on its 1.0 release yet.
See also:

2 Likes

I feel like this severely downplays how notoriously awful cross-compiling C is. If anything, it’s not as easy as zig [build|cc|cxx] --target <triple> or GOOS=... GOARCH=... go build.
Agreed on the lack of host/target separation being also a big obstacle though.
Who was leading opam-cross etc efforts? Maybe they can join us here and share a bit of their experience directly from the battle fields.

1 Like

I think there are several people I’ve seen post about cross-compilation. From my own experience doing cross-compiling with OCaml, the C compiler itself is a minor detail today in cross-compilation. I wrote a guide of sorts at Compiling OCaml in Depth — Diskuv OCaml 1.0.1 documentation (mostly to help me remember when I get lost; it is not a user doc). It is definitely not just drop in a C cross-compiler and voilà!

Super high-level summary: The OCaml native-code compiler generates assembly language instructions (text, not binary machine code) from your .ml files, which then gets compiled with an assembler into object files, and then linked to the pre-existing C runtime library. Talk of zig doing cross-compiling is missing the target-specific assembler that is invoked every time you use ocamlc.opt ocamlopt.opt.

4 Likes

but zig does bundle its own assembler and linker which is also cross-target… So I imagine, if a standard OCaml distribution (or opam) kept an OCaml source tree around, used zig to cross-compile the runtime, and OCaml itself was able to output different assembly depending on target, or you were able to compile different OCaml compilers depending on target, you’d either way get the target assembly which is assembled with the zig cross-assembler, then all would be linked together with the cross-linker, you’d be able to cross-compile OCaml code with a single invocation.
Also, it doesn’t have to go that far. Just being able to cross-compile ocamlrun, then bundle bytecode executables to all platforms, would be really nice.
That’s how I imagine it.

Even when I was rung paid for using it I was just looking for moving away from that position.

3 Likes

I’ll bite. What cross-assembler? Is https://ziglang.org/documentation/master/#Assembly outdated when it says:

… Some day Zig may have its own assembler. This would allow it to integrate more seamlessly into the language
… Output constraints are still considered to be unstable in Zig, and so LLVM documentation and GCC documentation must be used to understand the semantics. Note that some breaking changes to output constraints are planned with issue #215.
… Input constraints are still considered to be unstable in Zig, and so LLVM documentation and GCC documentation must be used to understand the semantics. Note that some breaking changes to output constraints are planned with issue #215.

According to the doc I cited above, Zig is a pass-through to LLVM and GCC assemblers, and the referenced issue #215 has been open for 5 years and 11 months. Instead of waiting for a Zig cross-platform assembler, why not use the cross-platform LLVM integrated assembler today? And if we narrow the meaning of “cross-assembler” to just mean a host machine’s assembler executable has a -target option that emits machine code for target hardware different from the host machine, then another today option would be to embed those -target cross-assemblers directly into OCaml.

You’d have to do the research to ensure that the LLVM abstraction - the LLVM integrated assembly language - is rich enough to support asmcomp, but if you want to contribute a LLVM backend to ocaml’s asmcomp I’m sure that would be useful. I’d use it!

1 Like

Oh I was just operating from memory, I remember zig having a zig as subcommand, but I could be wrong. Apologies. Also seeing zig doesn’t have a “zig assembler”, they’re still able to emit target arch’s format somehow? From my x86_64 system I could easily cross-compile to arm linux and darwin and whatever.

That does sound a lot less handwavey :smile:

Edit: you’re right, it doesn’t:

image

Also as you can see, Arm assembly works just fine

You already pointed out that LLVM is the one to thank for this. I still think it’s worth seeing a language runtime written in this language, for all the things it offers. I think it’s a valuable improvement over the status quo with C.

Disclaimer: this whole post is personal subjective opinion.

My belief is yes, sometimes it does make sense to choose Go over OCaml.

The choice has nothing to do with the language syntax itself 80% (or more) of the time.

Examples, when does it make sense to choose Go over OCaml:

  • The tool is very simple and can rely mostly on code existing in Go ecosystem.
  • The tool is trivial, open source, you want to share it but don’t want to manage it (releases etc.).
  • What you’re interested in is creating large open source community of contributors (100+).
  • You want a quick and dirty solution.

Little explanation for the above:

  • If you find a Go library on Github you want to try out, you just write code.
  • Some OCaml libraries do not work across minor versions.
  • Go is easier to learn. Go code is less dense and overwhelming for newcomers. Toolchains are far superior.

That said it’s not at all exhaustive list and there is as much reasons to choose OCaml over Go.

1 Like