3 ms·
Just to clarify for the crowd though. In F#, `List.head` throws an exception when it fails whereas `List.tryHead` returns an `option`, which returns `None` when
by bmitc 2y ago
Just to clarify for the crowd though. In F#, `List.head` throws an exception when it fails whereas `List.tryHead` returns an `option`, which returns `None` when it fails instead of an exception.
A general confusion of mine in Elixir is generally how libraries and functions treat errors. There's the common idiot of returning either `{:ok, ____}` or `{:error, ____}`, but what can be inside the error tuple is not always clear. The other thing is that sometimes a function can both throw an exception and also return a success tuple. Such cases are confusing to handle, and there's a large gap between handling cases like that and the philosophy of "let it crash", which I think is preached a little looser than it should actually be practiced.
I do like F#'s way of disambiguating the two situations. The only issue I have in F#, which actually exists in every language that I know of that has exceptions, is that there is no way to know, up front and clearly, what exceptions can be thrown by a given function. This is particularly frustrating in F#, which has fantastic pattern matching for exceptions. I wish there was exhaustive pattern matching in F# for exception handling, such that it would warn you that you have an unhandled exception in a try/with expression (https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/exception-handling/the-try-with-expression https://learn.microsoft.com/en-us/dotnet/fsharp/language-ref...) but of course would allow for wildcard patterns.
- IshKebab 2y agoJava has checked exceptions and they are used, e.g. in Android. But it seems to be the exception (heh) rather than the rule. IMO the problem is proper exception handling with checked exceptions and wrapping each function (or at least small blocks) in try catch is just so insanely verbose that even though it is possible to get error handling as good as something like Rust, nobody actually does it in practice.
- sodapopcan 2y ago> There's the common idiot of returning either `{:ok, ____}` or `{:error, ____}`, but what can be inside the error tuple is not always clear. That's the result of Elixir being dynamic and not having built-in monads. You always have to check the docs/code. > The other thing is that sometimes a function can both throw an exception and also return a success tuple. I would say this is just poor design (though maybe someone could point me to an exemplary use of this?). As I see it (which is from how I've read it described and how most good lib do it): - If function can error and the caller can do something about it, return :ok/:error. - If the caller can't do anything about the error, raise. - If the function can't fail, return a raw value.
- bmitc 2y agoIt's true that it's a poor library implementation, but it's also a fairly natural consequence of a language with exceptions plus some other way of handling errors. Even in a language like F#, just becauase a function returns an `option` type doesn't mean it won't also throw an exception sometimes. However, I don't necessarily think pure functional languages solve this either. If a Haskell function returns an option, you have little idea as to where the error originated. There are error types such as result types, which F# has as well, but then that's basically back to exceptions, perhaps even as a more limited form. I'm curious if there's a language that's really nailed error handling. Erlang/Elixir have their supervisors and functional languages have pattern matching on option types, result types, and exceptions, but surely there's a way to improve on that.