Комментарии:
Is it possible to have implicit conversions? For example you just return error, and it would autocast it to Result.Failure. I tried playing around with various libraries that have Result type, and sometimes implicit conversions work, sometimes it forces me to cast it.
ОтветитьThank you very much for the video. For viewers not wanting to "roll their own", I recommend the LanguageExt library, which implements all relevant types in a way that is consistent with functional programming conventions. The Result type demonstrated here roughly corresponds to Either<TLeft, TRight> in LanguageExt. The only difference is that, by convention, TLeft represents the failed case (e.g., ErrorB) and TRight the success case (e.g., int). LanguageExt also offers the Fin<TResult> type as "the" Result type. It is equivalent to Either<Error, TResult>, where Error is LanguageExt's built-in error type. Fin<TResult> is a little easier to handle. Either<TLeft, TRight> adds more options if required but, owing to the second type parameter (TLeft) for the failure case, is not as easy to handle as Fin<TResult>. Ultimately, it's a tradeoff, as always.
ОтветитьThank you Zoran!
ОтветитьThis is very clean. I wonder how you would make this work with IoC and multi-layer solutions: the domain/app layer would need to define all possible error types for the other layers to use, correct?
ОтветитьThis seems very similar to Rust error handling...
Ответитьi generally try not to throw exceptions bcuz they used to be a severe performance hit i'll usually just log the error and return false or set WasSuccessful to false 9/10 i don't care about the specific exception im just gonna try catch Exception end of day
Ответитьgood content!
ОтветитьI would like to see a nuanced discussion of the pros and cons of the pattern. Every video I've seen is saying "use result pattern instead of exceptions" but not a single one of them raise the CONS of using the pattern. And then we have the comments... Which sometimes contain quite a lot of criticisms such as "overengineering" or "unreadable", etc, which could be valid objections and should be addressed imo.
I have experience with it from OCaml (similar to F#). C#'s language support for monads such as this could be better. Better type inference, pattern matching and the pipe operator for example, making developer ergonomics and readability better.
Very good. I long figured that it's a nasty practice to throw exceptions everywhere. Usually I handle them as soon as possible, or return values to not need MORE try-catch blocks.
ОтветитьHi Zoran. Since the Right<L, R> monad represents a Maybe<L, R>, or Either<L, R>, shouldn't the error be on the left? "Right is right." Also, why use a Result instead of a straight up Maybe? I'm still new to FP. Just trying to figure all this out. I've been reading Paul Louth's higher kinds series. Takes me a couple days to comprehend each article, which are supposed to take about 20 minutes to read. lol
ОтветитьNow that you've mentioned such a topic as domain validation, I'm actually quite curious as to what you think are some best practices when doing it
ОтветитьWhile I am still having hard time not seeing the risks and boilerplate that comes with Result, and I still use Exceptions in my code, I must say this was the best hands-on example how to do it I've seen. It makes it look much better than virtually everyone else and their dog did, which was just... show nothing concrete. Zoran again delivers.
ОтветитьI see a lot of value when exception is propagated from bottom layer to higher layers. In essence, this is a serious error and when unhandled it should abort the program. In the end, there's an error in it!
Of course, be safe and handle such problems as close to the source, if possible! Like: file not found, index out of bounds, null pointer. And only if the problem really cannot be graciously handled then alert the others, possibly writing enough info to logs and terminating the process if necessary.
On the other hand, silencing exceptions (by using empty "catch" blocks) is extremely silly, to say the least. This is known as "gotta catch'em all" and can be foun on "Coding Horror" blog.
How about using instead one interesting clean code practice? Namely split long method which has both logic and exception handling into two: one public with error handling, and the second, private method (called from the first one) with logic but without exceptions at all? If the Single Responsibility Principle jumps to your mind, you are definitely correct 😁
Oh, and by the way. By specifying possible errors or failures as part of returned Result on the LEFT side of the method seems familiar! It is similar to Java checked exceptions, except (pun intended) Java has this "throws" keyword on the RIGHT side of the method.
Personally I consider Java checked exceptions a scientific experiment with mixed results. Even the "Advanced Java" book had this advice to inherit custom exceptions from RuntimeException, to avoid the noise of "throws", and "try, catch, finally" in the code.
Edit: one final BTW is that in Java there's also exception chaining, quite useful if used correctly.
That said, this video is pretty much thought provoking, and I thank you for posting it. Interesting material indeed 👍
them: "do not throw. do not throw"
me: "i'm gonna throw"
I have a love-hate relationship with result objects. Making them monadic is definitely a must, but I also found things get a bit ugly and unintuitive when using IAsyncEnumerable, or any deferred execution type, with result objects.
ОтветитьI wish they paid more attention to F# instead of us having to bend C# into a FP language
ОтветитьExcellent video! If you offer a paid course in C# that puts all your recommendations into practice, I would take it with great pleasure. I've been looking for a course that brings all these best practices together in a concrete project - like a REST API - but haven't found one. It would be really valuable to have such content.
ОтветитьWhen you map ErrorC to ErrorB you lose the stack trace that's terrible for debugging
A robust result type solution would need more work
The easiest thing is to make errors a linked list and make errors all implement an interface
But... why? What do we get from these custom errors unless we handle them differently, which is a very rare
usecase?
The only cases i can think of are form validation and super complex algorithms that change strategy on failure