Live data from Hacker News

OCaml as my primary language

xvw.lol

111–120 of 296 posts

Re: OCaml as my primary language

#111
post #55

Earlier quoted context omitted.

I'd say that Google strives to have a reasonably short list of languages approved for production-touching code. Rust can replace / complement C++, while OCaml cannot (it could replace Go instead... fat chance!). So I suspect that the team picked Rust because it was the only blessed language with ADTs, not because they won't like something with faster compile times. No way OCaml could have stolen the Rust's thunder: w…

Wouldn’t Kotlin be a more reasonable choice in that case? It has ADTs and a lot of the same niceties of Rust.

Yes. It can also be compiled to native. I just think it was held back too much by the java/jvm backwards compatibility but then again that's probably also the justification for its existence.

I definitely find it (and jetpack compose) make developing android apps a much better experience than it used to be.

What I like a lot about Kotlin are its well written documentation and the trailing lambdas feature. That is definitely directly OCaml inspired (though I also recently saw it in a newer language, the "use" feature in Gleam). But in Kotlin it looks nicer imo. Allows declarative code to look pretty much like json which makes it more beginner friendly than the use syntax.

But Kotlin doesn't really significantly stand out among Java, C#, Swift, Go, etc. And so it is kind of doomed to be a somewhat domain specific language imo.

Re: OCaml as my primary language

#112

Earlier quoted context omitted.

> This has the benefit of giving you the ability to refer to a case as its own type. A case of a sum-type is an expression (of the variety so-called a type constructor), of course it has a type. datatype shape = Circle of real | Rectangle of real * real | Point Circle : real -> shape Rectangle : real * real -> shape Point : () -> shape A case itself isn't a type, though it has a type. Thanks to pattern matching, you'…

> The moment you start ripping cases as distinct types out of the sum-type, you create the ability to side-step exhaustiveness and sum-types become useless in making invalid program states unrepresentable. Quite the opposite, that gives me the ability to explicitly express what kinds of values I might return. With your shape example, you cannot express in the type system "this function won't return a point". But with…

> Not sure about C#, but in Java if you write `sealed` correctly you won't need the catch-all throw.

Will the compiler check that you have handled all the cases still? (Genuinely unsure — not a Java programmer)

Re: OCaml as my primary language

#113
post #110

Earlier quoted context omitted.

Oh if you use those features to express what "sum type as subtyping" can, it sure gets confusing. But it's not those things that I want to express that are hard to reason about, the confusing part is the additions to the HM type system. A meta point: it seems to me that a lot of commenters in my thread don't know that vanilla HM cannot express subtypes. This allows the type system to "run backwards" and you have full…

Yes and my point was, when you want what you present in the first comment, quoting my post, you have tools for that, available in OCaml. But there is cases, when you do not want to treat each branch of your constructors "as a type", when the encoding of visitors is just rough. This is why I think it is nice to have sum type, to complete product type. So i am not sure why we are arguing :)

> So i am not sure why we are arguing :)

I think we agree on a lot of points. The rest is mostly preferences. Some other comments in my thread though...

Re: OCaml as my primary language

#114
post #72

Earlier quoted context omitted.

OCaml also needed the brief but bright ReasonML moment to add/fix/improve some of the syntax IIRC and work on user-friendly error messages. But this should've definitely happened much much earlier than it did.

I would say ReasonML also needed more follow-through. It seems like the OCaml community hasn't really rallied behind it.

It doesn't help that the OCaml community also has the problem that a significant minority seem to resent the fact that one company (Jane Street) has written more OCaml than the the rest of the world combined and then some and so de facto controls the ecosystem.

Whereas the Go and Rust communities, for example, were just fine with having corporate sponsorship driving things.

Re: OCaml as my primary language

#115

Earlier quoted context omitted.

> The moment you start ripping cases as distinct types out of the sum-type, you create the ability to side-step exhaustiveness and sum-types become useless in making invalid program states unrepresentable. Quite the opposite, that gives me the ability to explicitly express what kinds of values I might return. With your shape example, you cannot express in the type system "this function won't return a point". But with…

> Not sure about C#, but in Java if you write `sealed` correctly you won't need the catch-all throw. Will the compiler check that you have handled all the cases still? (Genuinely unsure — not a Java programmer)

Yes

https://openjdk.org/jeps/409#Sealed-classes-and-pattern-matc...

> with pattern matching for switch (JEP 406)the compiler can confirm that every permitted subclass of Shape is covered, so no default clause or other total pattern is needed. The compiler will, moreover, issue an error message if any of the three cases is missing

Re: OCaml as my primary language

#116
post #71

I'm sure there's merit to the language, but the syntax seems absolutely alien to me. Some attempt to look like verbose imperative code, a bunch of semicolons, and for some strange reason, hate of parenthesis. Real life sample: let print_expr exp = (* Local function definitions *) let open_paren prec op_prec = if prec > op_prec then print_string "(" in let close_paren prec op_prec = if prec > op_prec then print_string…

> That seems pretty hard to read at a glance, and easy to mistype as a definition.

YMMV but let expressions are one of the nice things about OCaml - the syntax is very clean in a way other languages aren't. Yes, the OCaml syntax has some warts, but let bindings aren't one of them.

It's also quite elegant if you consider how multi-argument let can be decomposed into repeated function application, and how that naturally leads to features like currying.

> Also, you need to end the declaration with `in`?

Not if it's a top level declaration.

It might make more sense if you think of the `in` as a scope operator, eg `let x = v in expr` makes `x` available in `expr`.

> Then, semicolons...

Single semicolons are syntactic sugar for unit return values. Eg,

  print_string "abc";

is the same as

  let () = print_string "abc" in

Re: OCaml as my primary language

#117
post #110

Earlier quoted context omitted.

Yes and my point was, when you want what you present in the first comment, quoting my post, you have tools for that, available in OCaml. But there is cases, when you do not want to treat each branch of your constructors "as a type", when the encoding of visitors is just rough. This is why I think it is nice to have sum type, to complete product type. So i am not sure why we are arguing :)

> So i am not sure why we are arguing :) I think we agree on a lot of points. The rest is mostly preferences. Some other comments in my thread though...

Ok! (BTW, thanks for the interaction!)

Re: OCaml as my primary language

#118

I saw a talk by someone from Google about their experiences using Rust in the Android team. Two points stuck out: they migrated many projects from Python, so performance can't have been that much of a concern, and in their surveys the features people liked most were basics like pattern matching and ADTs. My conclusion is that for a lot of tasks the benefit from Rust came from ML cicra 1990, not lifetimes etc. I feel…

> aesthetically the double semicolons are an abomination and irk me far more.

I think they have been optional for like 20 years, except in the top-level interactive environment to force execution.

That being said, I still don't get why people are so much upset with the syntax. You'll integrate it after a week writing OCaml code.

Re: OCaml as my primary language

#119
post #114

Earlier quoted context omitted.

I would say ReasonML also needed more follow-through. It seems like the OCaml community hasn't really rallied behind it.

It doesn't help that the OCaml community also has the problem that a significant minority seem to resent the fact that one company (Jane Street) has written more OCaml than the the rest of the world combined and then some and so de facto controls the ecosystem. Whereas the Go and Rust communities, for example, were just fine with having corporate sponsorship driving things.

> and so de facto controls the ecosystem

They really don't, less than 5% of opam packages depend on Base and that's their bottom controversial dependency I'd say. Barely anyone's complaining about their work on the OCaml platform or less opinionated libraries. I admit the feeling that they do lingers, but having used OCaml in anger for a few years I think it's a non-issue.

What they do seem to control is the learning pipeline, as a newcomer you find yourself somewhat funneled to Base, Core, etc. I tried them for a while, but eventually understood I don't really need them.

Re: OCaml as my primary language

#120

Earlier quoted context omitted.

> This has the benefit of giving you the ability to refer to a case as its own type. A case of a sum-type is an expression (of the variety so-called a type constructor), of course it has a type. datatype shape = Circle of real | Rectangle of real * real | Point Circle : real -> shape Rectangle : real * real -> shape Point : () -> shape A case itself isn't a type, though it has a type. Thanks to pattern matching, you'…

> The moment you start ripping cases as distinct types out of the sum-type, you create the ability to side-step exhaustiveness and sum-types become useless in making invalid program states unrepresentable. Quite the opposite, that gives me the ability to explicitly express what kinds of values I might return. With your shape example, you cannot express in the type system "this function won't return a point". But with…

> With your shape example, you cannot express in the type system "this function won't return a point".

Sure you can, that's just subtyping. If it returns a value that's not a point, the domain has changed from the shape type and you should probably indicate that.

  structure Shape = struct
    datatype shape =
        Circle of real
      | Rectangle of real * real
      | Point
  end

  structure Bound = struct
    datatype shape =
        Circle of real
      | Rectangle of real * real
  end
This is doing things quick and dirty. For this trivial example it's fine, and I think a good example of why making sum-types low friction is a good idea. It completely changes how you solve problems when they're fire and forget like this.

That's not to say it's the only way to solve this problem, though. And for heavy-duty problems, you typically write something like this using higher-kinded polymorphism:

  signature SHAPE_TYPE = sig
    datatype shape =
        Circle of real
      | Rectangle of real * real
      | Point

    val Circle : real -> shape
    val Rectangle : real * real -> shape
    val Point : shape
  end

  functor FullShape () : SHAPE_TYPE = struct
    datatype shape =
        Circle of real
      | Rectangle of real * real
      | Point

    val Circle = Circle
    val Rectangle = Rectangle
    val Point = Point
  end

  functor RemovePoint (S : SHAPE_TYPE) :> sig
    type shape
    val Circle : real -> shape
    val Rectangle : real * real -> shape
  end = struct
    type shape = S.shape
    val Circle = S.Circle
    val Rectangle = S.Rectangle
  end


  structure Shape = FullShape()
  structure Bound = RemovePoint(Shape)

This is extremely overkill for the example, but it also demonstrates a power you're not getting out of C# or Java without usage of reflection. This is closer to the system of inheritance, but it's a bit better designed. The added benefit here over reflection is that the same principle of "invalid program states are unrepresentable" applies here as well, because it's the exact same system being used. You'll also note that even though it's a fair bit closer conceptually to classes, the sum-type is still distinct.

Anyways, in both cases, this is now just:

  DoesNotReturnPoint : Shape.shape -> Bound.shape
Haskell has actual GADTs and proper higher kinded polymorphism, and a few other features where this all looks very different and much terser. Newer languages bake subtyping into the grammar.

> If they're not actual sum types but are pretty much the same, what good does the "actually" do?

Conflation of two different things here. The examples given are syntactically similar, and they're both treating the constituent part of the grammar as a tagged union. The case isn't any cleaner was the point.

However in the broader comparison between class hierarchies and sum-types? They're not similar at all. Classes can do some of the things that sum-types can do, but they're fundamentally different and encourage a completely different approach to problem-solving, conceptualization and project structure... in all but the most rudimentary examples. As I said, my 2nd example here is far closer to a class-hierarchy system than sum-types, though it's still very different. And again, underlining that because of the properties of sum-types, thanks to their specific formalization, they're capable of things class hierarchies aren't. Namely, enforcing valid program-states at a type-level. Somebody more familiar with object-oriented formalizations may be a better person to ask than me on why that is the case.

It's a pretty complicated space to talk about, because these type systems deviate on a very basic and fundamental level. Shit just doesn't translate well, and it's easy to find false friends. Like how the Japanese word for "name" sounds like the English word, despite not being a loan word.

Post reply on HN