Live data from Hacker News

OCaml: a Rust developer's first impressions

pthorpe92.github.io

51–60 of 157 posts

Re: OCaml: a Rust developer's first impressions

#51

Earlier quoted context omitted.

People say that OCaml is like Rust, but unlike Rust, OCaml has Exceptions that could appear everywhere. How is that safe?

Rust has panics that could appear anywhere.

But the flow control is easier to reason about. You don't have to go guessing about non-local catch blocks that the caller may have introduced. The code either panics, or propagates.

Exceptions look remarkably wrong headed to me in retrospect. Allowing the caller to change the error handling contract and flow control.

Re: OCaml: a Rust developer's first impressions

#52

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).

As you get more comfortable with functional programming, the type signatures tend to get more complex for sophisticated code. Those "nasty generics" become reliable friends. For example, since functions are first-class objects, they are often passed around as parameters to other "higher-order" functions. Explicitly declaring the types of all these functions is tedious at best, and confusing at worst.

I'm not convinced that these type signatures have to be that bad. You could probably make them pretty tenable, especially with a good code formatter.

Also, I'm confused. We're using type driven development and writing the type signatures as part of the documentation, we should be thinking in terms of types, why would they be difficult to write?

Re: OCaml: a Rust developer's first impressions

#53

Earlier quoted context omitted.

As you get more comfortable with functional programming, the type signatures tend to get more complex for sophisticated code. Those "nasty generics" become reliable friends. For example, since functions are first-class objects, they are often passed around as parameters to other "higher-order" functions. Explicitly declaring the types of all these functions is tedious at best, and confusing at worst.

I'm not convinced that these type signatures have to be that bad. You could probably make them pretty tenable, especially with a good code formatter. Also, I'm confused. We're using type driven development and writing the type signatures as part of the documentation, we should be thinking in terms of types, why would they be difficult to write?

This is a really good question. When you write generic code in a language with higher-order functions, type signatures can get pretty hairy with type variables and quantifiers flying around (forall a. a -> foo a). In a functional language with type inference, it's mostly managing those for you, and you can (usually) just write code in a natural way and it automatically is polymorphic, but in a safer way than dynamically-typed languages.

Re: OCaml: a Rust developer's first impressions

#54
post #25
post #20

Earlier quoted context omitted.

In OCaml the type checker won't force you to handle exceptions (from what I remember.) See e.g. https://ocaml.org/docs/error-handling#exceptions

That's true, but at least in my experience, it is rarely a problem. Because if you're at a point in your program where you don't want to bubble up, you can just pattern match against the exceptions just as you would a Result type, which F# also has. I don't know Rust, but after searching, it seems that it has a panic facility which seems even more escaping than an exception. Happy to be corrected there.

Yeah. I don't find it to be a problem either, but the parent does and I can see where they are coming from. Even Java has checked exceptions.

Re: OCaml: a Rust developer's first impressions

#55

Earlier quoted context omitted.

Yes and no, Rust is ML inspired but it is still way too imperative and not as functional to be called a true ML. But yes, things like Result and Option types, do-notion via "?," at least for Results and Options, algebraic data types are all part of the appeal. There sadly are still no higher kinded types though, but that is a difficult problem to solve and most won't encounter such problems anyway in day-to-day codin…

The ? thing, didn't that get borrow from Swift, in fact?

Creator of the Rust language Graydon Hoare is working on Swift, so it’s probably the other way around

Re: OCaml: a Rust developer's first impressions

#56

OCaml is great, we learned it in class back in college, then we learned Rust right afterwards. Oftentimes people say OCaml is Rust without the borrow checker (or that Rust is OCaml with a borrow checker), but that's not quite true. Since it is entirely functional and recursive, it takes more time to wrap your head around. Now with OCaml 5, we also have algebraic effect support, something that Rust is looking to add i…

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.

Re: OCaml: a Rust developer's first impressions

#57
post #38
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…

> It gets pretty annoying to type annotate everything for nothing but warm fuzzies when the compiler can figure it all out for you. This is sometimes fine for internal code, but for external interfaces you want a human to determine the interface contract you’re agreeing to, rather than the minimal set or the maximal set of types the function supports as currently-written.

Perhaps, but presumably code that is meant to have an external interface will be in a module and will have an *.mli file to it, and that is an excellent place to constrain your types explicitly if you must.

Re: OCaml: a Rust developer's first impressions

#58

Earlier quoted context omitted.

As you get more comfortable with functional programming, the type signatures tend to get more complex for sophisticated code. Those "nasty generics" become reliable friends. For example, since functions are first-class objects, they are often passed around as parameters to other "higher-order" functions. Explicitly declaring the types of all these functions is tedious at best, and confusing at worst.

I'm not convinced that these type signatures have to be that bad. You could probably make them pretty tenable, especially with a good code formatter. Also, I'm confused. We're using type driven development and writing the type signatures as part of the documentation, we should be thinking in terms of types, why would they be difficult to write?

They're not bad. They're just a pain to type out in detail every time. Here's an example signature from an F# library I like called FParsec:

    pipe2: Parser -> Parser -> ('a -> 'b -> 'c) -> Parser
That's fine for documentation, but I'm glad it's not cluttering up the actual implementation, which starts like this:

    let pipe2 (p1: Parser) (p2: Parser) f =
        ...
You can see how the code does explicitly specify the types of `p1` and `p2`, but doesn't bother spelling out the type of `f` or the return type. Although we are thinking in types the whole time, this sort of flexibility in the implementation is important for real-world functional code.

(For anyone wondering, this function takes two parsers and a function as input. It sends the output of each parser to the function, and the result is itself a parser. This is equivalent to a well-known abstract function called `liftA2` in a language that supports typeclasses, like Haskell.)

[0]: https://www.quanttec.com/fparsec/reference/primitives.html#m...

[1]: https://github.com/stephan-tolksdorf/fparsec/blob/master/FPa...

[2]: https://hoogle.haskell.org/?hoogle=liftA2

Re: OCaml: a Rust developer's first impressions

#59

OCaml is great, we learned it in class back in college, then we learned Rust right afterwards. Oftentimes people say OCaml is Rust without the borrow checker (or that Rust is OCaml with a borrow checker), but that's not quite true. Since it is entirely functional and recursive, it takes more time to wrap your head around. Now with OCaml 5, we also have algebraic effect support, something that Rust is looking to add i…

People say that OCaml is like Rust, but unlike Rust, OCaml has Exceptions that could appear everywhere. How is that safe?

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 overflow on debug builds)

In any case OCaml has memory safety, sort of (it has data races but as described in the paper "data races bounded in time and space", data races in OCaml doesn't lead to unrestricted UB like in Rust, C, C++, and most other languages actually) (unless you opt into OCaml's unsafe constructs like Obj.magic, which is like Rust's unsafe without using unsafe {} block), because it has a GC

So when you talk about safety in the context of exceptions, you probably mean exception safety rather than memory safety. Exception safety basically means you code works even if an exception is raised. Which is really hard to assure since as you said, exceptions are everywhere

But Rust suffer from this same problem! In Rust this is called panic safety and mitigating it has taken a great deal of complexity, with things like lock poisoning, which adds overhead but limit the scope of panics in multithreaded programs, and the UnwindSafe trait, which is probably a good attempt but is ignored in most of the ecosystem. Many people think such measures are inadequate and insufficient and prefer to run programs with panic=abort, which means to just terminate the program when there is any panic. (many C++ projects disable exceptions in the same way for example)

Which is kind of unfortunate because now there are many Rust programs that are only correct if you run with panic=abort, and will break if you enable stack unwinding (which is the default)

Re: OCaml: a Rust developer's first impressions

#60
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.
Post reply on HN