Live data from Hacker News

OCaml: a Rust developer's first impressions

pthorpe92.github.io

141–150 of 157 posts

Re: OCaml: a Rust developer's first impressions

#141
post #12

Yeah my semi-hot take is that type annotations in functions is a feature not a bug. It forces legible interfaces (with the exception of nasty generics I suppose).

But they can just be auto-generated in documentation. It gets pretty annoying to type annotate everything for nothing but warm fuzzies when the compiler can figure it all out for you. Also, any decent IDE will show the types any way. The complaint about the types is just strange and shows a failure in getting Rust out of their head. Once you get used to F# and OCaml, then going back to a language that forces type ann…

> because you spend all this time telling the compiler what it already knows

But a program should be optimized not for reading by a compiler but for reading by a human. This is essential for maintainability.

The question is not how hard it is for a compiler to deduce types, but how hard it is for a human (and a human that hasn't necessary written this ode, and hasn't necessary read and remembered the whole codebase) to figure it out.

It is not a simple question in general. Too much "infrastructure" clutter can hide the logic and intent of the code too. So to get the right balance in a language or in the code is non-trivial.

Re: OCaml: a Rust developer's first impressions

#142

Earlier quoted context omitted.

> It partially helps since it forces you to have types where they matters most: exported functions But the problém the OP has is not knowing the types when reading the source (in the .ml file). > How would it feels like to use list if only https://github.com/ocaml/ocaml/blob/trunk/stdlib/list.ml was available, If the signature where in the source file (which you can do in OCaml too), there would be no problem - which…

How do you do encapsulation without an mli? can you hide the type implementation to the module user? can you ensure the exported functions (which are all of them without an mli) maintain your type invariant? > the real problém is that the documentation is often worse too, as the .mli is autogenerated and documented afterwards Of course, that would defeat the purpose of having interfaces and is incredibly bad practice…

I find Rust is really effective in this regard. From my understanding of the capabilities of mli files, Rust's visibility modifiers are even more powerful in terms of effectively hiding implementation details and maintaining invariants, but they're all written inline in the source code (with the `pub` keyword).

I agree that the encapsulation capabilities of mli files are great, but I don't see why they need to be written in a separate file (apart from the obvious reason: that's how OCaml does it, and you can't just change a language's design just because you don't like one bit of it!)

Re: OCaml: a Rust developer's first impressions

#143

Earlier quoted context omitted.

Safety was something added to Rust as it developed, not one of the original goals. As I recall it. And you're working with multiple definitions of "safety" here, and Rust sorta conflates them all via borrow checker, but the one people are usually most concerned with is memory safety which is not a concern for a garbage collected language. I do seem to recall that StandardML did not have exceptions though. And I alway…

I agree, Standard ML's syntax feels a lot cleaner than that of OCamls.

I think of OCaml like a "kitchen sink" language, much like Scala. The reference ML implementations like SML and Jersey seem to be much more limited in scope, and are nice for learning / getting a feel for the original intentions behind the language family.

Re: OCaml: a Rust developer's first impressions

#144
post #139

Earlier quoted context omitted.

Java and C# will not provide any sanity check on your use of locks and shared mutable state though. But the Rust borrow checker will. It won't prevent deadlock or race conditions, but it will at least guide you there much better than those pass-by-mutable-reference OO languages will.

99.9% of all code written in these languages uses a thread per request model. Objects never go over a thread boundary. Using rust will only complicate these code bases for concerns that will never occur. …and if you’re concerned about mutability you can use an immutable first language like Scala or F#.

Sure, and that's the right way to do it. But if you cross the thread boundary, it won't stop you. Rust will at least try.

Re: OCaml: a Rust developer's first impressions

#145
post #9

i had some of the same problems; here's how i solved them, though keep in mind i am no ocaml expert, just a dabbler at some point i got sick of trying to debug mysterious compile errors about types in ocaml and started declaring types for all of my function arguments; ocaml lets you do that. an example is http://canonical.org/~kragen/sw/dev3/mukanren.ml (an implementation of a tiny logic programming language, sort of…

There is a vscode extension for the ocaml-lsp that will show type annotations above each line of code.

so, no keystrokes, but you can only fit half as much code on your screen?

Re: OCaml: a Rust developer's first impressions

#146
post #135

> Where are the types? OCaml supports type annotations practically anywhere, if you want to add them. Alternatively, you can use an editor that supports querying or showing types for bindings. VSCode and Emacs support this. > Remember recursion? How about linked lists? A lot of beginner material uses List to introduce algebraic data types, inductive and equational reasoning - but you don't have to use them. In fact,…

> A lot of beginner material uses List to introduce algebraic data types, inductive and equational reasoning - but you don't have to use them. In fact, the standard library encourages Seq instead over List. I have repeatedly found this the hardest part of learning OCaml - it seems really difficult to find a tutorial that explains how to write OCaml as a real developer would working with production OCaml. Like, I don'…

I would personally recommend trying out F#. :) Answers below are for F#.

> For example, which sequence type should I be using most often?

You should basically default to list. If you need assignment and indexing or a 2D structure, use array. If you're writing generic processing code, use sequences if you can. Use sequence if you need lazy sequences.

> And how often should I be using refs/mutability?

Only when you need. I most often use mutability when interacting with unmanaged code, translating some Python or other imperative code before transitioning it to be more functional, and inside classes to manage internal state. Otherwise, I don't.

> And there's lots of alternate stdlibs - should I be dipping into those?

I'm not familiar enough with the OCaml ecosystem, but F# doesn't have this problem. F# has its own core library and for everything else, you just reach out to .NET.

> What does unit testing typically look like for an OCaml project (and what about other forms of testing)?

I personally use FsUnit, specifically the XUnit flavor. https://fsprojects.github.io/FsUnit/xUnit.html. FsUnit is an F# DSL over NUnit and XUnit, so it fits in well with F# and functional thinking but also plugs into the IDEs and `dotnet test`.

Re: OCaml: a Rust developer's first impressions

#147

Can't comment on the comparison to Rust, but I recently spent quite some time learning OCaml, working through the excellent and freely available cs3110 course https://cs3110.github.io/textbook/cover.html ; I really really wanted to like the language... I agree w/ the submitted article about the heavy reliance on linked lists and recursion, but what disillusioned me from it is that after many weeks of study I discover…

Not all languages are equaly suitable to all tasks. If your leetcode easy problem is to write a quick sort, you wouldn't use linked list + recursion. That being said, the OCaml idiomatic solution is to declare a mutable array and write your for-loop, just like you would do in C++. > given everything's immutability OCaml has a lot of mutable datastructures. It's perfectly fine to use them. Even though with experience,…

> Not all languages are equaly suitable to all tasks.

That is true. Although, I would make the argument that F# and OCaml are two of the best most general purpose languages available. They support the imperative, OOP, and functional paradigms effectively equally. About the only thing you wouldn't use them for is embedded real-time programming. Other than that, they're statically typed with great type inference and sane scoping and syntax (I find F# to be the "cleaner" language) and are performant.

Re: OCaml: a Rust developer's first impressions

#148
post #12

Earlier quoted context omitted.

But they can just be auto-generated in documentation. It gets pretty annoying to type annotate everything for nothing but warm fuzzies when the compiler can figure it all out for you. Also, any decent IDE will show the types any way. The complaint about the types is just strange and shows a failure in getting Rust out of their head. Once you get used to F# and OCaml, then going back to a language that forces type ann…

> because you spend all this time telling the compiler what it already knows But a program should be optimized not for reading by a compiler but for reading by a human. This is essential for maintainability. The question is not how hard it is for a compiler to deduce types, but how hard it is for a human (and a human that hasn't necessary written this ode, and hasn't necessary read and remembered the whole codebase)…

> But a program should be optimized not for reading by a compiler but for reading by a human.

I absolutely agree, more than you could imagine, but I think this is not a case where that's the issue. Type annotating everything will quickly make the code harder to read. There's a reason why people like Python. But the problem with Python is that you don't have a way to figure out the types. With F#, just hover over anything and get the type.

> This is essential for maintainability.

Even more to the point, over type annotating will make maintenance harder, not easier. This is because you are fixing the types statically. When you then go to update code in the future, you have thrown out the benefits of type inference and now have to go around manually updating all of these type annotations.

So you took the quote a bit out of context. If you're type annotating code, you're telling the compiler something it already knows, you're telling the human reader what it can already find out immediately for any value, you're increasing the surface area of needed updates, and making the code harder to read.

Re: OCaml: a Rust developer's first impressions

#149
post #99

Earlier quoted context omitted.

Rust has panics as well and they appear pretty much everywhere, because the Rust stdlb made a conscious decision to panic on allocation failures (later non-panicking APIs were added, but most people dont use them, and in special, most dependencies will not use this and will panic on random occasions), and also because common operations like integer division and array indexing will panic on bugs (and also integer over…

Rust currently cannot reliably panic on allocation failures because the error object is inside a Box, which itself requires allocation. This means that if memory is tight, panicking itself might fail. For reference, this is the type that is returned from catch_unwind: https://doc.rust-lang.org/std/thread/type.Result.html But I completely agree that Rust has exceptions. They are even used in the toolchain implementati…

> Rust currently cannot reliably panic on allocation failures because the error object is inside a Box, which itself requires allocation. This means that if memory is tight, panicking itself might fail.

This seems like something that should be allocated at program startup, just like other things like the program's environment (I think it's copied to Rust's own data structures at startup to avoid using the non-threadsafe C API), and other things allocated at startup

.. but of course, not at Linux, such error allocation would be unneeded there..

.. except of you disable overcommit, which can and do happen, so in the general case you don't know if this error object can ever appear

Re: OCaml: a Rust developer's first impressions

#150

Earlier quoted context omitted.

I kinda just think of Rust as C++ with a vaguely MLish inspired syntax & some aspects of the type & module system -- which is sort of how it explicitly started TBH -- and the borrow checker just grew out of what you end up needing to do if you rip the garbage collector out of a language like that.

Rust was originally garbage collected. From Graydon Hoare's post, he didn't want explicit lifetimes originally either. He agreed because he thought they would always be inferred by the compiler. https://graydon2.dreamwidth.org/307291.html It seems to me that Rust evolved into a better C++, but it did not start out that way.

Early on I argued for bignums and decimals in Rust, imagining also things like automatic currying, and other FP features

But it went a completely different way, ripping out any runtime it had, making reference counting a lot more noisy, etc.

It would be interesting to build a "simpler Rust"

Post reply on HN