Open ebresafegaga opened 4 years ago
I like this idea, but it would be even nicer if the completeness checker could use information of the function to help telling if we miss an exception (something like Java has had for a long time). That would require adding raising exceptions to the meta data, or tagging functions with something like RaisesAttribute
(in case it cannot be determined statically easily).
The same completeness check for exceptions could be added to try/with.
Maybe the rule ought to be that all exception cases should come last. I wonder how the same syntax should be used in conjunction with F# exceptions (the ones you can already match over).
It should be noted that OCaml has this exact feature.
The argument in that post that it helps with tail recursion inside a try is a good one. 👍
@ebresafegaga, you may want to add that to the pros, it's a strong argument, I think.
The argument in that post that it helps with tail recursion inside a try is a good one.
@ebresafegaga, you may want to add that to the pros, it's a strong argument, I think.
Thanks, done!
A more orthogonal (and simpler to imlement) variation is that what follows exception
is a standard exception pattern, e.g.
let printer i =
match l.[i] with
| n -> printfn "Item: %d" n
| exception (:? ArgumentException as e) -> printfn "Invalid index" e.ArgumentName
| exception e -> printfn "An error unknown occurred: %s" e.Message
This is important for two reasons
It doesn't introduce a new category of patterns like "ArgumentException" (though that could conceivably be a separate, orthogonal suggestion, applicable to try/with as well)
It is often necessary to bind exceptions at their more specific types. Note that in F# exceptions are actually types, whereas in OCaml the are a different category of tags (you can't use an exception tag as a type).
BTW I'm sold on this feature solely because it helps people write more accurate code where the exception handling doesn't cover too many things. I don't think it's particularly beautiful nor for beginners - it's for more advanced users
BTW those interested in programming language archeology may want to know that AFAIK the first people to propose this kind of construct were Nick Benton and Andrew Kennedy at Microsoft Research (though they integrated with let
not match
). I'm not totally sure the OCaml people are even aware of that :)
https://www.cs.tufts.edu/~nr/cs257/archive/nick-benton/exceptional-syntax.pdf
I marked this approved in principle, subject to this comment https://github.com/fsharp/fslang-suggestions/issues/895#issuecomment-670927823
I'm not planning to work on it myself though may be able to give some guidance. I don't think it would be too hard to implement
A more orthogonal (and simpler to imlement) variation is that what follows
exception
is a standard exception pattern, e.g.let printer i = match l.[i] with | n -> printfn "Item: %d" n | exception (:? ArgumentException as e) -> printfn "Invalid index" e.ArgumentName | exception e -> printfn "An error unknown occurred: %s" e.Message
This is important for two reasons
1. It doesn't introduce a new category of patterns like "ArgumentException" (though that could conceivably be a separate, orthogonal suggestion, applicable to try/with as well) 2. It is often necessary to bind exceptions at their more specific types. Note that in F# exceptions are actually types, whereas in OCaml the are a different category of tags (you can't use an exception tag as a type).
BTW I'm sold on this feature solely because it helps people write more accurate code where the exception handling doesn't cover too many things. I don't think it's particularly beautiful nor for beginners - it's for more advanced users
I totally agree with you.
BTW those interested in programming language archeology may want to know that AFAIK the first people to propose this kind of construct were Nick Benton and Andrew Kennedy at Microsoft Research (though they integrated with
let
notmatch
). I'm not totally sure the OCaml people are even aware of that :)https://www.cs.tufts.edu/~nr/cs257/archive/nick-benton/exceptional-syntax.pdf
Awesome! A lot of great ideas have come from MSR. I'll definitely check this out.
I'd very much like to help with implementing this. For a while now I've been trying to hack on the F# compiler, but it seemed so daunting. So I think this would be a good opportunity for me to do that and I would definitely need some guidance. Thanks!.
@ebresafegaga The next step would be an RFC here: https://github.com/fsharp/fslang-design
Although starting off with some coding isn't a bad idea either, it's often a good idea to flesh out a preliminary design before diving into a trial implementation. You can look at RFCs like https://github.com/fsharp/fslang-design/blob/master/RFCs/FS-1090-Generic-struct-type-whose-fields-are-all-unmanaged-types-is-unmanaged.md as an example of the level of detail.
There's an important difference between the 2 snippets. The second one catches exceptions in when
guards, clause expressions and active patterns, whereas the first one, supposedly, doesn't.
Exception handling shouldn't be part of your typical program flow.
Would this encourage writing manual exception handling more, when the business logic program flow should be handled without rising exceptions?
Typically F# attitude has been failwith, fail fast and hard, don't try to recover.
I know there are libraries raising exceptions like business-as-usual, but that will not justify this, just like C# libraries returning nulls don't justify C# becoming language optimized for null-handling: more and more keywords to make anti-patterns easier.
Not only performance implications, exception-handling is like goto-clauses causing random not-clear paths between nodes of your program. Besides that they make debugging .NET harder, when you can't just break on each CLR exception.
I think the feature should mention that it works the same for the function
keyword, just in case this would not be obvious.
@Thorium while I think vanilla f# we love doesn't lean strongly on exception declaration nor finegrained handling via try/with
; in context of larger integration with .net idioms, the richness and expressivity of f#, for this type of interop (many f# usage also happens in mixed dotnet codebases), doesn't remove to the soundness of the core of the language, when it stands mostly on its own.
There are trade-offs, but f# and I assume ocaml earlier on, endorsed usage of exceptions, unlike go, rust, some dialect of c++.
Having this supported, doesn't preclude to, one day, having an f# checker / attribute, that would enable an "pure exceptionless f#" ecosystem (for soundness or performance reasons), but keeping in mind that f# is foremost used on runtimes that endorse usage of exceptions, and that there are wins in expressivity with this feature, while still making exception
keyword very very visible, for this suggestion.
You may be interest in my point about exceptions, in this feature suggestion: https://github.com/fsharp/fslang-suggestions/issues/830.
Side note, maybe f# semantic colouring should have something similar to mutable, around all constructs related to exceptions (failwith
functions, exception types, try/with/finally)?
@smoothdeveloper
I think the feature should mention that it works the same for the
function
keyword, just in case this would not be obvious.
Is that correct? In the case of function
isn't it too late at that point? e.g. if I pipe something into a function
the compiler can't at that point wrap a try..with
around whatever generated the value. But in the case of match expr with
it can transform it into a try..(match expr with)..with
and split the exception and non-exception cases between the two with
sections.
Unless what you're piping into the function is a Result
type that has a value or an exception and then you could have it unwrap everything from that.
@realparadyne you are right for the match input! That said, @kerams comment also gave me some food for thoughts, I think the exception occuring on when
beside the match input, should also be covered; in my understanding, this would apply to function
.
Those nuances make me a bit uncomfortable along same line as @Thorium, but still think, the construct for total match, including any exception that occurs in evaluation, from top to bottom, in a single scope has value for expressivity, enough to not down vote the issue.
I think the RFC should put emphasis on aspects pertaining to how introducing exception
patterns interacts with"total match" checks that are in the current version of the language, and maybe considering https://github.com/fsharp/fslang-suggestions/issues/731 as well.
Aside, what about match!
?
I propose we allow pattern matching for exceptions in match expressions when the expression to be matched is a function call (or indexer/ property) For example,
The existing way of approaching this problem in F# is :
Even though this isn't bad I think the former is clearer, more succinct and even easier to read (say, a new team member)
Pros and Cons
The advantages of making this adjustment to F# are
The disadvantages of making this adjustment to F# are
Extra information
Estimated cost (XS, S, M, L, XL, XXL): M
Affidavit (please submit!)
Please tick this by placing a cross in the box:
Please tick all that apply: