Hacker News new | ask | show | jobs
by mjdowney 1147 days ago
Related: https://twitter.com/zetalyrae/status/1639474931086901248

My OCaml:

    let foo (x: bar): baz =
      (\* Is this quuxable? *)
      match is_quuxable x with
      | Yes ->
         (* Then, we have to ... \*)

All the OCaml I see in the wild:

    qlet%bind f = 's 't  'a 'b (fun ꙮ -> ꙮ ((<_<')) ꙮ)
    [@@ppx_gov_backdoor (~b~e~p~i~s)]
4 comments

Functional programming community likes this stuff. They go into the classical mathematicians’ trap of writing more and more general versions of something even though it’s not necessary.
I think this is partly because of the syntax.

When we write human languages (that use a Latin script), we use punctuation to delineate the beginning and end of terms. In a language like C or Rust, terms are surrounded by commas, parentheses, angle brackets, and so on. In OCaml and Haskell, many things that there is syntax for in C-style languages are done as ordinary function calls, which separate terms by only whitespace.

It is easier to read this (Rust):

  let value = some_long_ass_function_name(some_quirky_parameter, another_one);

  another_function(value)
than this (Haskell):

  let value = someLongAssFunctionName someQuirkyParameter anotherOne
  in anotherFunction value
or this (OCaml):

  let value = some_long_ass_function_name some_quirky_parameter another_one in
  another_function value
Individual terms in camel case are difficult to read if they consist of more than a couple words. Consecutive terms in snake case are difficult to read because underscores and whitespace look alike visually.

Haskell and OCaml's idiomatic solution is to use extremely terse names for arguments, type variables, and local variables, out of what seems to be syntactic necessity.

That's because of currying. If you want to partially apply parameters, in your example, with Haskell we would just have to do this:

        someLongAssFunctionName someQuirkyParameter
And we get a function that accepts one parameter. But in Rust you would have to do this:

        move |another_one: i32| {
            some_long_ass_function_name(some_quirky_parameter, another_one)
        }
Which is not easier to read.
It is true that currying looks comparatively awful in Rust. But that is not because Rust uses parentheses and commas. A hypothetical C-style language could use dedicated syntax for currying, like

  some_long_ass_function_name(some_quirky_parameter, ..)
which would be more readable without giving up on punctuation.
Scala does that, with `_` being a "hole in the expression". I wouldn't call it currying though.

  func(param1, _)
Every once in a while you have to make some change to that expression.

  // you may want to write
  func(param1, func2(_))
  // but you need to write the full lambda
  param2 => func(param1, func2(param2))
Ah, now I realized, you're right. The lack of visual distinction between different syntactic elements is what's killing me.
I don't think this is correlated to functional programming. Programmers in general tend to over-architect because we're often learning as we're going and sometimes it's more fun to use some cool concepts. Since design patterns started becoming en vogue back in the day, OOP codebases got filled with those patterns. Now we are left picking up the pieces with things like `AbstractProxyFactoryBean` or whatever. Same thing with JavaScript where that ecosystem is infamous for using tons and tons of npm packages to recreate simple functionality, e.g. 'left-pad a string'. Arcane mathematics-heavy FP code is just how that expresses in FP languages. And in fact, OCaml is actually an antidote to that (for the most part), because it deliberately offers a lower level of abstraction than Haskell. I frequently say that OCaml is like a cross between Haskell and Go.
I don’t know OCaml, or really any language that would help me fully understand the code, but my exposure to OCaml is this stuff, and it looks pretty clean to me. https://github.com/janestreet/base

Of course, I haven’t read every file, so maybe I got lucky with my random sampling.

It's a pretty practical language, so much so that its creators think it's actually a great language to teach Unix programming, and even wrote a book about it: https://ocaml.github.io/ocamlunix/ocamlunix.html

Excerpt:

> Tradition dictates that Unix system programming must be done in C. For this course we found it more interesting to use a higher-level language, namely OCaml, to explain the fundamentals of Unix system programming.

> The OCaml interface to Unix system calls is more abstract. Instead of encoding everything in terms of integers and bit fields as in C, OCaml uses the whole power of the ML type system to clearly represent the arguments and return values of system calls. Hence, it becomes easier to explain the semantics of the calls instead of losing oneself explaining how the arguments and the results have to be en/decoded. (See, for example, the presentation of the system call wait, page ??.)

> Furthermore, due to the static type system and the clarity of its primitives, it is safer to program in OCaml than in C. The experienced C programmer may see these benefits as useless luxury, however they are crucial for the inexperienced audience of this course.

> A second goal of this exposition of system programming is to show OCaml performing in a domain out of its usual applications in theorem proving, compilation and symbolic computation. The outcome of the experiment is rather positive, thanks to OCaml’s solid imperative kernel and its other novel aspects like parametric polymorphism, higher-order functions and exceptions. It also shows that instead of applicative and imperative programming being mutually exclusive, their combination makes it possible to integrate in the same program complex symbolic computations and a good interface with the operating system.

Until they get enlightened.

Related: https://willamette.edu/~fruehr/haskell/evolution.html

Hmm. I haven't seen the latter in OCaml as much. More the opposite; when I first used it, things like list length in the standard library weren't tail recursive. 8-|

Now, Haskell and shudder Scala shudder, on the other hand.

Compared to Haskell, OCaml developers are generally not to fond of custom operators which shield you from some of the worst cases unless you are reading code an Haskeller wrote in OCaml which happens.

For a long time, most of the users were either writing compilers or static analysers for low level languages and a fair share was very knowledgeable in C and system programming. It had a huge impact on what was seen as idiomatic. You can still find trace of it in for exemple the system library which looks more like the C stdlib than a modern standard library or how the compiler finds libraries.

Generally the community used to have a very different "flavour" than the Haskell one (it was also very French to be fair). I think I t’s less true nowadays. Still I would fall from my chair if I ever encounter someone working on the OCaml compiler writing a disparaging disingenuous post on Haskell. Meanwhile, we are here which reflects exactly my general experience with the Haskell community.

Ppx are used more and more often however and it did make code harder to read.

> Still I would fall from my chair if I ever encounter someone working on the OCaml compiler writing a disparaging disingenuous post on Haskell.

This exactly. OCaml devs uniformly respect Haskell and give it its due props for pushing innovation and commercialization in FP. We just think OCaml is pragmatic especially if you ever need to scale up your codebase and team.

Oh, I like Haskell a lot. I'm a bit leery of using it because performance and memory use are still a bit of a dark art, though much less than they used to be.

I just like mocking Haskellers (and Scala) for the symbols. :-)

The latter code looks totally quuxable.
Nah, I think it's already been quuxed up.
Thanks! Whenever I see a generic type i cry a bit.