leostera / caramel

:candy: a functional language for building type-safe, scalable, and maintainable applications
https://caramel.run
Apache License 2.0
1.05k stars 25 forks source link

Handling of Errors #43

Open leostera opened 3 years ago

leostera commented 3 years ago

After some consideration, I think I'd like error handling to be closer to Rust:

Panics are actual terminations, and would fit well with the supervision model of OTP. A Panic is a guaranteed process restart by the supervisor.

This would also mean forbidding all remnants of try / catch / exceptions from the language, and treating panic as a reserved word.

For interop with Elixir, stuff with a bang (!) is known to panic, stuff without it is expected not to. Human convention here.

For interop with Erlang, we don't have a clear indicator, but we should consider providing a wrapper to turn an Erlang error into a Result. This would help reuse some of the erlang module functions that just blow up on bad inputs (I'm looking at you *_to_*).

nicobao commented 3 years ago

I quite like Rust style of Error Handling.

However, will Caramel still be a strict subset of OCaml if it adds a new reserved keyword and forbids try.. catch construct? Or do you mean adding this restriction when using the Erlang OCaml library?

To me, Caramel being at least a subset of OCaml is important for compatibility reasons. I would want my Caramel code to work in Reason/OCaml and existing OCaml/Reason mostly work with Caramel. Otherwise, if the point of Caramel is to have a type-safe Erlang, why not using Gleam?

One of the main selling point of Caramel to me is being able to write business logic in Caramel and share it with a Reason/Rescript frontend. I know that it is not necessarily recommended to share too much logic between front and back - but I often end up wanting sharing functions to manipulate DTO, alongside the DTO itself (example: a traversal algorithm alongside a tree DTO).

leostera commented 3 years ago

Excellent point! Yes, all Caramel code should be valid OCaml code syntactically.

Adding a reserved word in Caramel is fine because that word will still be valid OCaml code, it would just be an undefined symbol that needs to be polyfilled. Another way of looking at it is that panic is just a Caramel standard library function like exit

Adding new syntax, however, would be breaking. This we can do via ppx's (preprocessing extensions), but won't do at the parser level.

Restricting the syntax is also what makes Caramel a strict subset of OCaml. Right now the OO system is not available, for example. If we decide to provide a panic function, we can also restrict the exception handling.

leostera commented 3 years ago

When reusing Caramel code on the frontend, if it panics, you could get an exception and handle it in Reason code like you handle anything else.

But in a Caramel-only codebase, you are forced to handle errors via result instead, making it more uniform. If something panics, the supervision hierarchy will take care of restarting it.

leostera commented 3 years ago

existing OCaml/Reason mostly work with Caramel

I see what you mean! Existing code that's throwing exceptions with raise will get a compiler warning/error saying that "raise" is unsupported.

Existing code that's handling exceptions just won't compile.

This will be a divide in the ecosystem, so we should explore it and make a very conscious decision.

michallepicki commented 3 years ago

For division by zero the plan is to panic like Ocaml does?

For the result types used / promoted in the standard library, I had hopes that the Ocaml ecosystem has some patterns to steal that would be "more typed" than just (x, string) result everywhere, but still convenient and composable to use. Maybe leveraging polymorphic variants or row polymorphism somehow?

leostera commented 3 years ago

Yes, I think setting up the polyvariant errors as a default is a good idea to allow easy error return composition.

I was thinking we can follow this pattern: https://github.com/ostera/reason-design-patterns/blob/master/patterns/polyvariant-error-propagation.md

leostera commented 3 years ago

That and the let+/and+ syntax is fundamental to writing happy-paths without losing track of errors. Very much like Rust's ? operator.

leostera commented 3 years ago

@michallepicki For division by zero the plan is to panic like Ocaml does?

Arithmetic and other runtime-provided functionality will end up having the semantics of the Erlang runtime. So division by zero is a panic (exception).

λ erl
Erlang/OTP 23 [erts-11.1.5] [source] [64-bit] [smp:64:64] [ds:64:64:10] [async-threads:1] [hipe]

Eshell V11.1.5  (abort with ^G)
1> 
1> 1 / 0.
** exception error: an error occurred when evaluating an arithmetic expression
     in operator  '/'/2
        called as 1 / 0