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

aha. OK, for sure, the OCaml ecosystem is more-constrained than the Golang ecosystem, simply b/c it isn’t used commercially like Golang is. Which … brings us back to “if you need to get paid, and that’s the price …”

I mean, in the mid-90s, Windows was the commercial choice, and all UNIXes were … “boy you guys just can’t get over it, can you? you lost, you pointy-headed nerds – you lost”. Didn’t change the technical reality, nor the commercial reality.

1 Like

I’m not sure it’s as viable as the one that starts with power-on reset and ends with cold halt.

Oh indeed. I remember sitting outside the 1369 coffeeshop in Cambridge, MA, and finding out that the person sharing the table used to be a hardware troubleshooter for SUN Microsystems. They told me about how part of the standard questions for a hardware support ticket, were:

  • current and historical humidity
  • ditto air pressure
  • elevation (above sea level)
  • and of course, temperature history (even if in a DC, still useful)

B/c hey, heat, condensation, other factors matter too.

Used to be hardware (and esp. networks) were far more unreliable than they are today. Even today, they’re not perfect.

1 Like

This can be a misleading metric. No-one needed to update because no-one had the time and determination to look in detail, or because the code is nearly free of bugs?
I used to think that usually the latter would apply, but recently I’ve looked at enough old code that hasn’t been touched in ~years (in OCaml) and what I discovered is that if you look close enough there are plenty of bugs, just not the kind of bugs you’d find in a C program or Python program.

So now my new approach when looking at code that wasn’t changed in a long time is:

  • assume it is broken. Look at the code until you can prove to yourself it isn’t, or find (a potentially) small improvement to make it more robust.
  • test it, try the easiest “stress” test you can imagine, usually leads to more bugs being discovered
  • why hasn’t anyone worked on this code? Is there something that would drive contributors away? (lack of documentation, lack of tests, lack of of a good build-system, etc.)

I’m very grateful that using OCaml eliminates certain classes of bugs, but one shouldn’t get overconfident, you can still find lots of bugs in OCaml code that are:

  • security bugs, e.g. resource consumption/DoS, lack of privilege checks, or bypassing of privilege checks, etc. You can structure your program to make it less likely to have these kinds of bugs, e.g. always do security checks first, and then run the rest of the code. If the code mixes security checks with actual implementation all bets are off.
  • simple typos that cause logic bugs (a missing parenthesis/begin in an ‘if’, a ‘> 0’ that should’ve been ‘>= 0’, etc.)
  • overuse of lists, leading to issue with non-tail-recursive maps or performance issues when they get larger (although things have improved now that Seq exists). Just because something can be represented as an immutable list, it doesn’t mean it is the best data structure for the problem at hand
  • resource leaks (e.g. file descriptor)

To do a more fair comparison of languages you’d have to compare code that was written with similar quality criteria (level of review/documentation/testing) and that has made similar good choices in what data structures to use (depending on what suits the language the best) and look at defect density then.
And that would be very hard to accomplish.

Once code grows beyond a certain size I think we might just have to accept it’ll have bugs, what matters more is:

  • how easy it is to find the bugs
  • how easy it is to reason about the behaviour of programs by just reading it?
  • once a bug is found can you fix it in such a way to avoid (or reduce likelihood of) that class of bugs in your code in the future? (e.g. by taking advantage of a language feature, type system, better libraries, etc.)
  • how confident can you be that a certain class of bugs is absent from your code?
5 Likes

It makes sense if you need to go… :wink:

2 Likes

I really think you’re placing a lot of faith in social sciences here. I really don’t believe there is a fair way to test the things you propose to test in the sense that there cannot be a neutral control group. I tend to view these kinds of studies more as large piles of anecdotal evidence—and really, I think reason and anecdotal evidence is the best we can do when choosing a programming language. It’s never going to be a science.

But earlier you did provide some good reasons why one might want to use Go: ecosystem, learning curve, company culture, etc., and I think you do well to point out that our assumption that OCaml’s type system / support for immutability is game-changing is not scientific.

I just doubt that testing these hypotheses will yield anything beyond additional anecdotal evidence.

2 Likes

Maybe you should read the papers first before dismissing them, to get some insight of their methods?

I mean, the meta-studies you posted seemed to suggest that, while certain metrics may be better than others for fault prediction, the results in different studies tend to vary quite a bit and no single metric or even set of metrics are reliable for fault prediction in an industrial setting, especially in post-release software.

This is kind of a recurring problem in studies that try to evaluate the products of human labor. There are too many variables.

Pah. Just use big enough data. Then the errors are normally distributed. :wink:

1 Like

Sorry for resurrecting this thread. I had read this already a while back, and I was curious about what you don’t like about OCaml’s UNIX interface. I use it for a lot of automation “scripts” and I find it works well for me. My impression that Unix programming was a priority for the core development team, but perhaps I’m mistaken.

2 Likes

First, I detest Golang with the fire of a thousand suns, and I personally don’t think it’s a great UNIX-scripting language. But many people do feel that way, and I guess I don’t want to argue with them: sure, knock yourselves out, Pike-heads, have at it. That said, I’ve been writing scripts of varying complexity that interact with UNIX for decades, and even though I’m an OCaml bigot, I’ve never been able to write them in OCaml without suffering a massive labor-productivity hit, as compared to writing them in Perl. I write very, very disciplined Perl, to the point where I really do know the type of every variable, and yet it’s much faster to write Perl scripts up to a screenful or so, than to write the equivalent OCaml program.

I don’t have to like this, for it to be true.

10 Likes

A little more about what I think is lacking in OCaml, for UNIX programming. UNIX programming means processes and syscalls, but more than that, it means programming the filesystem. It means a really lightweight interface to files – their contents – and to text. I routinely write extremely concise programs in Perl that do significant things to the contents of files: reading, pattern-matching, building new lines, outputting to new files, creating filenames from patterns, and on and on. I know that Python programmers feel the same way (though, heh, I don’t agree with them about Python, grin). OCaml just isn’t as concise, and concision is its own form of comprehensibility.

ETA: Maybe it’s also worth mentioning something else: often one starts with a really small script – a bash script – and when it reaches half a screenful, you translate that to Perl (or Python, sure, Python grin). Then it grows and grows and grows. At some point, you might want to rewrite that script in OCaml. But by the time it gets complex enough to warrant that conversion, it’s pretty big, and the cost of conversion is greater than the cost of ongoing maintenance. This happens all the time, and the result is that you have scripts that really ought to have been OCaml programs, but aren’t.

The problem here (at least, for me) is that the initial cost of writing short OCaml programs that work like small Perl/Python programs is too large. If that initial cost were lower, I’d start in OCaml. And just to be clear, I don’t mean stuff like perl -p -i -e or perl -n. That’s just a simple outer loop, or a simple iteration across commandline file arguments. I mean the more-complex stuff, like globbing, regex, variable-binding, etc.

2 Likes

Can you show some example of this, when it’s easier or more concise to write in Perl or Python rather than OCaml? Not sure I get it, especially considering OCaml is pretty terse for a statically typed language.

I don’t have one to hand at this moment. I’ll dig around – surely I can find one somewhere …

Aha. Thanks for clarifying. I thought you meant something was missing from OCaml’s Unix interface, but I think I see what you mean.

If Perl is the basis of comparison, just about every language is cumbersome and verbose by comparison. Perl was designed specifically for this. Perhaps the only terser language is BASH (and company).

I am one of those who uses Python for these kinds of things for the most part, though I understand it’s not as terse or intuitive as Perl for handling text files. I just use it rather than Perl because I know it very well and never have to look anything up when writing such a program. It’s also said that Python is easier to maintain than Perl, which is certainly true in my case, but I’m sure people who know Perl very well may feel differently.

I do write Unix “scripts” things in OCaml sometimes these days, and it definitely takes a bit longer than Python, but the main slowdown is that I occasionally need to refer to the documentation. The length of the programs is usually about the same—which is, of course, a bit longer than the equivalent Perl program.

3 Likes

OK, here’s an example. It’s a simple script, called ya-wrap-ocamlfind (“yet another ocamlfind wrapper”, $(YAWRAP)). I’d like to add this support to ocamlfind, but … ugh, I fear writing this code in OCaml would be a PITA. It took me a few minutes to write this script in Perl, and then some more time to integrate it into my most-recent OCaml project, where it materially simplified the Makefiles. I’ve wanted to have something like this for a good while, but always resisted b/c ocamlfind already parses the command, builds a new command, and execs that. To put yet another wrapper ahead of ocamlfind seems like waste. But now that I have it, I think I’ll keep it, b/c it does simplify things a lot.

Here’s how you use it:

  1. suppose you have a Makefile line (in an implicit rule) like:
$(OCAMLFIND) ocamlc $(DEBUG) $(WARNERR) $(OCAMLCFLAGS) -package $(PACKAGES) -c $<
  1. Now, different files can be in different syntaxes (“camlp5o” or “camlp5r”) but ALSO, some files need special extra arguments (different ocamlfind packages, maybe extra arguments for the ppx_import (or pa_ppx_import) PPX rewriter). Maybe for other PPX rewriters, too. So some files need a different Makefile build-line. But really, the most complicated it gets is to add
-syntax camlp5o -package pa_ppx.import,pa_ppx_migrate -ppopt -pa_import-I -ppopt .

to the build-line. So you could imagine that the file had this line in a start-of-file comment, viz.

(** -syntax camlp5o -package pa_ppx.import,pa_ppx_migrate -ppopt -pa_import-I -ppopt . *)

and then the makefile line was:

	$(YAWRAP) $(OCAMLFIND) ocamlc $(DEBUG) $(WARNERR) $(OCAMLCFLAGS) -package $(PACKAGES) -c $<

And now $(YAWRAP) will take the cmdline, treat the last argument as a file (or, if there’s an argument --, then all args after that), pull out the first line of the file if a comment of the form above, and append the contents of the comment to the command. If there’s more than one file, it’ll do that one-by-one to each file. And then execute those commands. Cheap and easy.

I’m imagining going further: those extra arguments really could be shortened by using a Makefile/environment variable

export IMPORT_CFLAGS=-ppopt -pa_import-I -ppopt .

and using that environment variable in the comment – but that would require extending the script to do simple parsing for variable-occurrences and expanding them. Which is a few lines in Perl, but not sure how much it’d be in OCaml.

For reference, here’s the script.

#!/usr/bin/env perl

use strict ;
use String::ShellQuote ;

our @cmd ;
our @files ; 

while (@ARGV) {
  if ($ARGV[0] eq "--") { shift @ARGV ; @files = @ARGV ; last ; }
  elsif (int(@ARGV) == 1) {
    @files = @ARGV ;
    last ;
  }
  else { push(@cmd, shift @ARGV) ; }
}


{
  @cmd = map { shell_quote($_) } @cmd ;
  foreach my $f (@files) {
    my $extra = discover_args($f) ;
    my $cmd = "@cmd $extra $f\n" ;
    print STDERR $cmd ;
    system($cmd) ;
  }
}

sub discover_args {
  my $f = shift ;
  open(F, "<$f") || die "$0: cannot open $f for read (to sense extra args)" ;
  my $line1 = <F> ;
  close(F) ;
  if ($line1 =~ m,^\(\*\*(.*?)\*\),) {
    return $1 ;
  }
  else {
    return "" ;
  }
}

I’ll note that every variable is properly scoped, everything has a fixed type, so really all I’m getting from Perl here is concision.

P.S. it uses String::ShellQuote to shell-quote the command itself before concating everything together into a string that it passes to the shell – so the command is effectively not reparsed, but the extra text is reparsed. Details, details, details.
P.P.S. This is pretty idiomatic Perl: I’m not using any cleverness – and typically I never use cleverness when writing Perl.
P.P.P.S. The “standard” PPX rewriters sometimes need extra arguments, too (besides ppx_import which needs them to find CMO files and MLI files): for instance the “inline-test” rewriters need 'em (as I learned when implementing pa_ppx workalikes – so I could run the originals and my workalikes and compare results).

Isn’t this a simple case of “the biggest enemy to a perfect solution [OCaml] is a good-enough solution [Perl + expert Perl knowledge] already in place”?

Not sure what you mean.

  1. I was an expert caml-light programmer long before I ever thought of programming in Python, and an expert Python programmer (down in the runtime) long before I thought of programming in Perl.
  2. this little tool – I wrote it yesterday in response to Aaron’s question.
    3, I’m a very experienced Perl programmer, sure. But I consciously avoid using too much of the language, and I can assure you that I have worked with truly expert Perl programmers (guys who’ve written books on Perl) and they all use much more of the language.

If it were as easy to write such tools in OCaml, as it is in Perl, I would write them in OCaml.

Maybe what I mean is: sure, we can find reasons for writing-off this little tool. We can find reasons for writing off every single example of something that’s easier to do in Perl, than in OCaml. That leads to only one place.

So in that example there are two features of Perl helping with the concision: 1) the regular expression match operator; 2) string interpolation. Both of these are central features of Perl, which makes sense because Perl is an untyped language. (A third related feature of Perl often contributes to concision: the bare word syntax for string literals. And a fourth does so less often: the eval keyword.)

All of these features could be glued onto the side of OCaml via the PPX mechanism, but they still wouldn’t be central like they are in Perl. To give them that kind of centrality, I’d think you’d need to implement them directly in the compiler, and I doubt that’s ever going to be a focus for the compiler folks.

1 Like

Isn’t that just trivially true that not all languages can excel in all use-cases? I don’t really buy the idea of a “general-purpose language”, personally.