←back to thread

157 points matt_d | 8 comments | | HN request time: 0.356s | source | bottom
1. kubb ◴[] No.45136229[source]
For people using OCaml, there’s one thing that kinda discourages me in it, that is exceptions as part of the API in the standard library.

Because exceptions aren’t checked, this effectively means that a language designed for type safety has as much type safety as python, because it’s very easy to forget handling something, and get runtime errors.

How do you deal with this day to day? I assume it’s impossible to just believe that all the code you pull in doesn’t use exceptions?

replies(4): >>45136795 #>>45137250 #>>45137299 #>>45142057 #
2. yodsanklai ◴[] No.45136795[source]
> as much type safety as python

That's an exaggeration.

You can use error types / monads like you would do in Rust/Haskell. When you use the Core standard library, you can use function who don't throw exceptions. Those who do use specific name conventions (foobar_exn).

3. debugnik ◴[] No.45137250[source]
> as much type safety as python

There's no type unsafety from unchecked exceptions, because uncaught exceptions are not unsound. Even Haskell has them (error and undefined), because from a theoretical standpoint they're equivalent to reaching an infinite loop. (Now, recovering from an exception isn't unsound either, but it might mess with your usual mutable invariants.)

In more practical terms, concerning overall correctness, OCaml has been adding option-returning variants of those functions, so most exceptions raised from the stdlib nowadays are much more likely to be intended by the author.

replies(1): >>45139713 #
4. johnisgood ◴[] No.45137299[source]
Your comment does not make much sense even if it is true.

Factor (Forth-like language) implements even its own ":" (defines a word, i.e. a function) using the language itself, it is not builtin, same with "if", and so forth. Thus, "MEMO:" or locals[1] ("::") being implemented as a library does not mean it is a bad thing, on the contrary, in the case of Factor, it makes it quite powerful. The object system is entirely implemented in Factor, too. "Large chunks of functionality are not part of the core language, they are in just as library".[2]

And to compare OCaml's type system to Python's is straight out absurd.

[1] Locals are entirely implemented in Factor, too, which is only about ~500 lines of code. It is not part of the core language, and on top of that, there is no performance penalty whatsoever!

[2] See more here: https://www.youtube.com/watch?v=f_0QlhYlS8g.

5. ux266478 ◴[] No.45139713[source]
I don't think Haskell is a good language to model our idea of error handling off of. It's one of many bugbears I have with that language, that it uses the Maybe monad as an error type. It technically works, but doesn't provide a meaningful distinction between "This function might not return anything, and this is defined behavior" and "This function has a singularity". MonadError exists, but I can't think of anywhere it shows up without digging deep into dragon caves of the compiler. Everything a normal user is going to touch will deal exclusively in Maybes.

I'm not a fan of Rust as a language for many reasons, but I will give it credit for making proper usage of the Result monad. They could have abused Option the same way Haskell abuses Maybe, but they didn't.

replies(2): >>45140539 #>>45157015 #
6. debugnik ◴[] No.45140539{3}[source]
I was just using Haskell's reputation to push back on the "as much type safety as python" hot take.

> [Haskell] doesn't provide a meaningful distinction between "This function might not return anything, and this is defined behavior" and "This function has a singularity"

I think Haskellers should fear divergence less, or push for SPARK-like static checking. In OCaml, the current trend would be to represent "not return anything" as None; and "has a singularity" by raising Invalid_argument or similar when the singularity check was considered a precondition, or returning Error (or an equivalent variant) for expected inputs.

Usage of Result in OCaml is also growing, thankfully. It's part of the stdlib, and we can use binding operators (let* foo = result) to do the same as ? in Rust (or let! in F#). OCaml 5.4 is even adding a Result.Syntax module so we can just open it instead of defining (let*) ourselves.

On the other hand, Result doesn't give us backtraces, and composes badly with other combinators or imperative flow. In my current project I'm instead giving a try to an effectul result_scope/get_ok API, which composes better.

7. yawaramin ◴[] No.45142057[source]
Every mainstream language has exceptions. Everyone knows how to use exceptions. They're easy to use and get the job done. OCaml suffers no type safety issues from the use of exceptions. It also has option and result types so people who need more control flow can use those. The OCaml standard library typically uses exceptions for real exceptional conditions like eg trying to access a key that doesn't exist in a map. Even Rust has panics which are basically exceptions.

You criticized Haskell as not a great example of error handling. Well, Erlang/Elixir also have exceptions, and they are considered the industry leader in error recovery.

Exceptions are actually fine, it doesn't really take much to install handlers which take care of catching, logging, telemetry, re-raising etc. They mostly get a bad rep because of the latest fashions in the PL space.

8. akkad33 ◴[] No.45157015{3}[source]
> I'm not a fan of Rust as a language for many reasons, but I will give it credit for making proper usage of the Result

Rust also has exceptions aka panics