Really great video with very clear example! Also better than your earlier video "Don't throw exceptions in C#. Do this instead". Also with a better nuget package, which is definitely worth to try. Of course having the actuall tagged unions implemented in C# would be better, resulting in more fine grained possibilities with pattern matching. And I think it would also be great to see a better type aliasing solution in C#. because falling back to inheritance and a ton of code generation is still not the nicest solution. But it's all still really great for now.
I was just pondering ways to do multiple but exclusive return types, and this kind of thing is actually perfect. On the face of it, it's exactly what I wanted, but even better
Coming from Rust, I think this is amazing and it is making me want to try out C#. It's more dynamic than Rust is capable of being which I think is perfect considering the immediate performance cost switching to C# from Rust, but it's promising to me that the more integrated runtime and environment is being used to provide features like this.
Absolutely will use DUs when they are implemented in C# with exhaustive matching, already use them constantly in F#. They are even more powerful for domain modeling. I don't see myself using OneOf though.
This is a neat feature. But it looked very familiar. This video made me realize that a discriminated union solves the same problem that enums solve in Rust. Rust enums are very powerful so this is an exciting feature for C#! Yes Rust code is littered with enums and match statements, which leads to very declarative code, I'm very happy to hear we will get something very similar in native C# in the future.
Yes, I prefer Rust matching and it’s optional type. Discriminated unions here start to approach the power of the optional type in Rust, with Some and None. Kotlin and Swift do something similar in the mobile space, with the generic Result type. Languages seem to be moving away from null in general
In the world of rust the most talked about things seem to be speed and the borrow checker, but I'd argue that one of the most interesting things about rust is it's lack of null, and a type system that forces you to defie and check for every possible state. Basically yes, rust enums are amazing
Very interesting. At work I'm building an API written in Go. To solve the same issue, I created a monad (as close as Go generics allow) which can either be "not set", "error", or "value". I made it so I could use it for internal fields as well and apply constraints for checking maximum, minimum, is empty, is unique, to set defaults if not set, and so on. It works for what the company needed but I wasn't sure it was a great solution. After seeing this, I realized two things; my solution isn't as elegant (mostly because of Go) and it was an acceptable solution, not something crazy I shouldn't have attempted. Thanks
Love this and completely agree with your opinion on hidden, magical functionality of Filters or other Pipeline middleware for error handling. The language features force you to handle error conditions where previously (many moons ago) the only way to ensure it didn't get missed was exceptions at runtime.
I found your channel yesterday. I am simultaneously ecstatic and overwhelmed to dive in to your content, you're brilliant. Do you have a recommendation on when to use minimal apis vs controller apis?
@@MisterNOmercy Thank you for the feedback! I recently built a minimal api and found a solid way of organizing it, so if that's what is likely to get more support in the future I may want no keep working on that I suppose.
There are two things I would love if it worked with this. 1) Actual switch expression, the match method - while functional - is similar to constructors in that they are one big method with semi-arbitrary values based on ordering, while switch expressions are more like object initializes 2) generic alias types. `Maybe = OneOf` Both of these things require compiler-level changes - especially the latter; not 100% sure if the former is possible with some weird wrapper type combined with Roslyn trickery
I guess same as typical Result but more of a deconstructed version & allowing you to perform result checks in a lamda expression. Both approaches allows you to return the desired type or result but this approach looks more natural probably with additional magic.
I have always found using a F# project for models (in a C# solution) to be a very good solution. With this I could even do records before C# had records. I use F# a lot, and even have some solutions where the API, domain, shared/kernel are all F# and just use C# for UI, EF.Core and the few places I prefer it. My biggest hope if C# created Union type is that there is some sort of compatibility with the Unions in F# already. It would be nice to pass unions between the language and have them both act natural.
@@AbrahamJLR I don't have anything on my public github, but it should be easy for you to put a silly example together. I will try remember something as well. I am replying to this first thing in the morning, so I might forget through the day, shout gain if you want me to do something. Or if you come up with something send it as well. I am very new to F# and any learning is good.
@@obinnaokafor6252 But then they should put those back into F# and make it a IL/.Net feature. I am just thinking it would really be annoying if C# treated an F# Union as fancy abstract and some concrete, and F# ended up doing the same with C# Unions, and the benefit could not really be felt cross language.
I'm a really big fan of discriminated unions ever since I learnt about them while developing in Swift. In my humble opinion, Swift has a much cleaner solution though: enums! You can add different parameters to each individual case and extract the arguments when switching on the case. Absolutely brilliant!
@@the_wilferine absolutely! I keep telling this to my colleagues and they look at my like “wtf are you complaining about” but once you’ve used it, it just makes so much sense.
Wow this is very helpful. I usually returning as Tuple
ปีที่แล้ว +4
Thank you for the video. Without having DU directly in C# it does seem like the next best thing. I would personally use it, however I do think it's a harder 'sell' in a larger team ... but that will always be a problem with 'altnernative' solutions :)
This is very similar to the Result enum in Rust, I'm happy that there's a way to get something like that in C#. Having errors as values should be a must in any modern programming language nowadays.
Typescript has the advantage of its types being erased at runtime - by the virtue of seating on top of a dynamic runtime, it has that flexibility with structural typing.
I would use it. This is great for writing backend APIs for writing business rules where you have to return multiple result types. Thanks for this video.
It would have been great if we could use an alias for our OneOf long type in the top part of the file, instead of class inheritance there.Everything else is cool. I would like a next video explaining in depth why this approach is better than throwing builtin/custom exceptions.
> It would have been great if we could use an alias for our OneOf long type in the top part of the file using SomeName= Namespaces.OneOf; If you have some fixed set of types for the whole project, then you can make global alias: global using SomeName= Namespaces.OneOf; > explaining in depth why this approach is better than throwing builtin/custom exceptions. Cons of Exceptions: Performance - exceptions are expensive. Especially, if some branch of code execution is an expected and frequent case. Implicit - It's impossible to know from method's signature what exceptions method throws. Because of the previous: No compiler safety - compiler cannot guarantee caller checks all cases. Easy to break Liskov's principle. When an exception is thrown, components up the call stack may end up having corrupted state.
One possible justification is that it's certainly better if you have fallback actions for error scenarios. Task.WhenAll is a good case for this as well, because you don't have to remember to implement fishing the exceptions out from the individual tasks.
@@nickchapsas That's true. I think I prefer it because I can use the pattern matching / 'inline' deconstruction and it looks more like F#. But I understand how it's more error prone.
Unions in TS and value enums in Rust are such great features that theyre the only reason I don't use c# for basically everything. If c# had them natively, Itd be basically perfect
I come from Dart and there are actually a couple of packages tackling the functional approach. Have been using Options and Either, pattern matching seems to be added soon. I see the big advantage in a cleaner control flow, no exceptions flying around to be catches etc. still a beginner in c#, this looks kind of familiar👍 Nice explanation, thks
I effectively implemented exactly that pattern for something at work, but the use of implicit operators was considered a bit too magic, and went against the company value of "simple is better than clever" too much. Now I have an explicit ResultOrError.Success(T) or ResultOrError.Fail(Errors) sort of thing. BTW: R# and Rider can show inline type info stuff, and that includes a little icon wherever an implicit operator is used. PS: Check out R# and Rider's extended colours - colouring things by their semantic meaning, so things like const, enum value, parameter, local mutable var, local singly-assigned var can all be different colours; similarly Class vs Static Class vs Struct vs Record etc.
i use a base controller class with control flow in the basic crud endpoints, validate (fluent), map (automapper), send to service (usually works for non crud endpoints too). maybe not the best possible way but it's clean and easy to follow. this seems less obfuscated, certainly, but at the cost of adding a lot of 'noise' imo.
I think this is great - I like self-describing code, and I think this goes a long way towards that - I also strongly dislike error-handling for expected conditions, and I feel this makes the code a lot more dry
What the video is missing is the comparison with the current best approach without external dependencies. A common interface for possible results can often be used.
I'd love to see result.Match replaced with a contract mapping that decides the result code based on either an underlying interface, or static mapping to some of the OneOf types. Also interested in the Swagger/OpenAPI support for this - definitely will have a play around with this package - thanks for sharing!
I am torn on this one. On the one hand i like the descriptiveness of the method signatures. But on the other hand i worry about readability. This adds error checking and mapping code everywhere while with exceptions you just dont see it. Also how composable is a codebase when all methods can return unions, how do you deal with errors comming from a few layers away? I kinda like exceptions, they dont get in the way
That is exactly the problem that DU's solve, you DON'T see the places it's likely to break. He gave a really good example of it I think, where from the outside of that function it was impossible to tell with certainty that it actually was going to behave in 3 ways, when it really looks like it can only behave in 2. Also he pointed out that that returned bool was not really very definitive either, it was an indication of the truth or falsehood of... Hopefully the whole thing being successful, but simply to succeed or fail is lacking context entirely. There is only usually one way for something to succeed, and very typically many ways to fail, and if you don't have a good idea of why you might have failed, then your program simply cannot be expected to behave in a way that isn't accounted for. You're completely right in that there is more visually to digest, but in a CSharp situation it's not really much of a choice for there not to be, unless you simply don't care about the programming api to be as clear as possible about it's underlying complexity. And that's the core of the problem, what's being described here is an issue of inherent complexity, there is no avoiding the fact that by the nature of the request being made, it has 3 behaviors at minimum. To change that it would have to be broken up somehow, and it might not be feasible to break it up in some situations. If you care quite a bit about readable code that still completely expresses the domain behavior, (and you care to entertain recommendations that you didn't ask for, lol) I highly recommend looking at using FSharp. The solution he's using an external library for in CSharp is part of the basic structure of the language in FSharp. It's a first class citizen there, and it makes so much of a difference to have the language built around the feature you're using. I recommend the fantastic beginner course by Matthew Crews on the FastFSharp channel. Also www.fsharpforfunandprofit.com is killer.
I guess I am the only one who would prefer to write DUs by hand. Just create a struct with private constructor, turn all the value constructors of the case classes to public factory methods, and you are 80% done.. Now you have your own type with no dependency on an external package.
This also make sure developers don't miss handling any return value and this is enforced compile time which is neat And code becomes explicit as well because there is no magic that Dev has to discover as to where does this exception is handled
I love the concept, it feels like F#. I would love to organize my code like that. However not sure how I feel about using that while it's not built-in.
I am not a fan of the match or switch method that require to declare behaviour in the same order than the declared generics. It makes for a terse syntax but it , somewhat, couple the implementation with the consumer. I can see the apeal though. I will have to test it out to see if it is really a problem or if i'm just grumpy this morning :D I did not know of this techniques, thank you!
Hello, Nick! Are you able to use this with a Validation Pipeline in mediator? In a way so we don't need to repeat in our services every method that needs validation or something. Also, can we use something like a result filter so we don't need to always use match for all our controllers actions? Thanks
Sure you can but that doesn’t make it a good approach. Having one type per result and having the user to explicitly handle, alongside all the relevant type information is a better approach
Nice one. Out of this video, can you please create a video on how to use fluent validations as attribute at the top of the class if possible? Thanks for this video.
The Result type is just one of the possible things too can do with OneOf. LanguageExt is great if you are planning to use things like Result, Option, Union etc. not just result
@@nickchapsas So use the Result in conjunction with OneOf. I was just a bit confused as it seemed the main use case of result is handled with OneOf and matching, as you replace exceptions with types/validation, though I could see if getting a specific value or an operation causes an exception Result still would be useful along with OneOf.
I have tried to use this package some time ago but it did not handle serialization and deserialization. I ended up implementing a similar class that can do serialization. My context is Azure Durable Functions where serialization is heavily used when returning from Activity Functions. I hope that the official implementation will be able to handle serialization correctly.
For many situations I've found the simple approach of returning a tuple indicating (a) success/failure and (b) relevant object upon success ... works well. Sort of a poor man's version of this video. Since you only look at (b) if (a) is true, doesn't matter much what the default value of (b) is in case of failure.
Waiting for c# to add it to the language with syntactic sugar.. I just hope it's gonna be just the right amount of sugar and the right type. Wonder how much time it will take to add this simple concept
Since switching to Python, I am getting unable to program whithout discriminated unions. I've been advocating that languages basically converge now, and here we have yet another example of convergence between C# and Python.
it's not directly related to the topic here but I've got a question to anyone who'd be interested: if you make a web service call in a method, would you handle the exception within the method for 500 or network failure and populate a failed result which is defined in return type, or would you not handle and expect it to be handled somewhere else?
Great video Nick, as always! The OneOf library doesn't seem to play very nicely with return types of IEnumerable though. Is this an issue with my design, should i wrap IEnumerables in a container model? Seems a bit of fudge to get it to work with this library. I currently use CSharpfunctionalExtensions but like the idea of being able to return more than 2 result types.
I really want someday C# will copy the "inline" of Kotlin, it would be huge and absolutely amazing for things like OneOf and dunet, because it will reduce greatly the overhead of calling these lambdas instead just placing the code right there
What is the overall “cost” of the switch statement and in your opinion when do things go a bit bat shit crazy with switch statements of 10+ possible results lol, it seems to “shortcut” right to the right place but is this an expensive operation or actually highly efficient switching?
I think you would only have to answer the question of how that would be able to express the return of a movie or not found? Or how would you return a detailed error or a success? That's the advantage of OneOf.
@@tomheijtink8688 you return the enum type and then in the parent method you receive the type and check with an if, switch or whatever the type and then the endpoint return the class, will be mostly the same than OneOf, I dont see a really true advantage with OneOf aproach
@Nick Chapsas feature is really great, but what you forgot to mention is that this library don`t work with interfaces. I`ve also cheked ValueOf lib. Any solutions to do the same with interfaces? for the Ienumerable I can call .ToList(). But is it possible to do it without ToList?
I really like the idea of discriminated unions, the service looks more cleaner without exceptions being thrown, etc. But what I don't like is a Match method with a set of lambdas in the controller, this looks somelike ugly. I would prefer the following JS-like syntax more: var (movie, notFound, validationError) = await service.UpdateAsync(); and then you check if you have a movie - return Ok(), if not found - return NotFound(), otherwise BadRequest(). Again it's not ideal but for me it looks better than a set of lambdas. Btw, Nick have you checked the performance when using OneOf comparing to versions that can return multiple values in one class but without throwing exceptions ? How much overhead do the lambdas have?
IMHO OneOf is excellent to represent alternative types but it is less beneficial to model a processing pipeline (with failures). In these cases a Result monad is more expressive and its composability is a major value. Something like Result should be more effective.
I like this a lot but i would rather see this implemented on the client side. That way i can have a distinct expected switch for each api call. Oneof movie,notfound,validation,exception so i can do similar to a promise and have reusable handlers. Essentially a quick use switch because switch doesnt understand multiple return types.
Really great video with very clear example! Also better than your earlier video "Don't throw exceptions in C#. Do this instead". Also with a better nuget package, which is definitely worth to try.
Of course having the actuall tagged unions implemented in C# would be better, resulting in more fine grained possibilities with pattern matching.
And I think it would also be great to see a better type aliasing solution in C#. because falling back to inheritance and a ton of code generation is still not the nicest solution.
But it's all still really great for now.
I saw the notification and dropped everything I was doing to watch this immediately!
I was just pondering ways to do multiple but exclusive return types, and this kind of thing is actually perfect.
On the face of it, it's exactly what I wanted, but even better
Coming from Rust, I think this is amazing and it is making me want to try out C#. It's more dynamic than Rust is capable of being which I think is perfect considering the immediate performance cost switching to C# from Rust, but it's promising to me that the more integrated runtime and environment is being used to provide features like this.
If you like this, then you'll also probably like Kotlin.
@@dzllz I mean probably, at the end of the day a tool is a tool. That being said, excitement is good!
@@driedurchin type inferring in C# sucks too much making functional programming and its derived variations pain in the ass
You'll love F#. It's also derived from ML like rust. It's also pretty safe, but without the borrow checker.
Man, I remember watching Nick the Greek back in the day. One of the best movies of all time and still cracks me up even to this day. Good times!
it's one of my favorites for sure
Absolutely will use DUs when they are implemented in C# with exhaustive matching, already use them constantly in F#. They are even more powerful for domain modeling. I don't see myself using OneOf though.
This is a neat feature. But it looked very familiar. This video made me realize that a discriminated union solves the same problem that enums solve in Rust. Rust enums are very powerful so this is an exciting feature for C#! Yes Rust code is littered with enums and match statements, which leads to very declarative code, I'm very happy to hear we will get something very similar in native C# in the future.
Yes, I prefer Rust matching and it’s optional type. Discriminated unions here start to approach the power of the optional type in Rust, with Some and None. Kotlin and Swift do something similar in the mobile space, with the generic Result type. Languages seem to be moving away from null in general
In the world of rust the most talked about things seem to be speed and the borrow checker, but I'd argue that one of the most interesting things about rust is it's lack of null, and a type system that forces you to defie and check for every possible state. Basically yes, rust enums are amazing
Very interesting. At work I'm building an API written in Go. To solve the same issue, I created a monad (as close as Go generics allow) which can either be "not set", "error", or "value". I made it so I could use it for internal fields as well and apply constraints for checking maximum, minimum, is empty, is unique, to set defaults if not set, and so on. It works for what the company needed but I wasn't sure it was a great solution. After seeing this, I realized two things; my solution isn't as elegant (mostly because of Go) and it was an acceptable solution, not something crazy I shouldn't have attempted. Thanks
Love this and completely agree with your opinion on hidden, magical functionality of Filters or other Pipeline middleware for error handling. The language features force you to handle error conditions where previously (many moons ago) the only way to ensure it didn't get missed was exceptions at runtime.
I found your channel yesterday. I am simultaneously ecstatic and overwhelmed to dive in to your content, you're brilliant.
Do you have a recommendation on when to use minimal apis vs controller apis?
Check out FastEndpoints. There's a video about it on this channel.
@@MisterNOmercy Thank you for the feedback! I recently built a minimal api and found a solid way of organizing it, so if that's what is likely to get more support in the future I may want no keep working on that I suppose.
Thanks for the headups for discrimated unions. One of the best examples here.
To anyone watching this video. Nick's REST API course is well worth taking.
There are two things I would love if it worked with this.
1) Actual switch expression, the match method - while functional - is similar to constructors in that they are one big method with semi-arbitrary values based on ordering, while switch expressions are more like object initializes
2) generic alias types. `Maybe = OneOf`
Both of these things require compiler-level changes - especially the latter; not 100% sure if the former is possible with some weird wrapper type combined with Roslyn trickery
2 - Yeah I also noticed the missing Maybe (or Option) type. Maybe (pun intended) you could derive it: class Maybe : OneOfBase ?
I guess same as typical Result but more of a deconstructed version & allowing you to perform result checks in a lamda expression. Both approaches allows you to return the desired type or result but this approach looks more natural probably with additional magic.
I have always found using a F# project for models (in a C# solution) to be a very good solution.
With this I could even do records before C# had records.
I use F# a lot, and even have some solutions where the API, domain, shared/kernel are all F# and just use C# for UI, EF.Core and the few places I prefer it.
My biggest hope if C# created Union type is that there is some sort of compatibility with the Unions in F# already.
It would be nice to pass unions between the language and have them both act natural.
F# union is just a compiler tricks.
@@JonathanPeel A lot involves some changes in the runtime and even modification if the IL
Jonathan, Can you share any repositories where interop is present using F# as the solution domain?
I am very curious about the implementation.
@@AbrahamJLR I don't have anything on my public github, but it should be easy for you to put a silly example together.
I will try remember something as well.
I am replying to this first thing in the morning, so I might forget through the day, shout gain if you want me to do something.
Or if you come up with something send it as well. I am very new to F# and any learning is good.
@@obinnaokafor6252 But then they should put those back into F# and make it a IL/.Net feature.
I am just thinking it would really be annoying if C# treated an F# Union as fancy abstract and some concrete, and F# ended up doing the same with C# Unions, and the benefit could not really be felt cross language.
I'm a really big fan of discriminated unions ever since I learnt about them while developing in Swift. In my humble opinion, Swift has a much cleaner solution though: enums! You can add different parameters to each individual case and extract the arguments when switching on the case. Absolutely brilliant!
Completely agree. This is one of my biggest missed features from Swift
@@the_wilferine absolutely! I keep telling this to my colleagues and they look at my like “wtf are you complaining about” but once you’ve used it, it just makes so much sense.
nice, i enjoy the fast pace, no wasting time. Discriminate union is looking useful
Wow this is very helpful. I usually returning as Tuple
Thank you for the video. Without having DU directly in C# it does seem like the next best thing. I would personally use it, however I do think it's a harder 'sell' in a larger team ... but that will always be a problem with 'altnernative' solutions :)
This is very similar to the Result enum in Rust, I'm happy that there's a way to get something like that in C#. Having errors as values should be a must in any modern programming language nowadays.
This is really cool. I just had a case use for this. Due to the code being flexible I had multiple fail levels, warning levels, etc.
The type system of typescript has spoiled me
Languages that support DUs ❤️
Typescript has a completely different type system, structural one, not nominal
Typescript has the advantage of its types being erased at runtime - by the virtue of seating on top of a dynamic runtime, it has that flexibility with structural typing.
@Miguel Yes, TypeScript is a better JavaScript.
Very lovely library. Would so much love to see this supported natively in C#
Would be interesting to see a benchmark comparing the exception flow with the union flow performance wise
I’ve already made that video. Check my "don’t throw exceptions" video
I would use it. This is great for writing backend APIs for writing business rules where you have to return multiple result types. Thanks for this video.
It would have been great if we could use an alias for our OneOf long type in the top part of the file, instead of class inheritance there.Everything else is cool. I would like a next video explaining in depth why this approach is better than throwing builtin/custom exceptions.
> It would have been great if we could use an alias for our OneOf long type in the top part of the file
using SomeName= Namespaces.OneOf;
If you have some fixed set of types for the whole project, then you can make global alias:
global using SomeName= Namespaces.OneOf;
> explaining in depth why this approach is better than throwing builtin/custom exceptions.
Cons of Exceptions:
Performance - exceptions are expensive. Especially, if some branch of code execution is an expected and frequent case.
Implicit - It's impossible to know from method's signature what exceptions method throws.
Because of the previous: No compiler safety - compiler cannot guarantee caller checks all cases.
Easy to break Liskov's principle.
When an exception is thrown, components up the call stack may end up having corrupted state.
One possible justification is that it's certainly better if you have fallback actions for error scenarios. Task.WhenAll is a good case for this as well, because you don't have to remember to implement fishing the exceptions out from the individual tasks.
You can use an empty abstract base record type instead. Works great.
You aren't forced to handle every implementation and you don't know where this class is inherited into
@@nickchapsas That's true. I think I prefer it because I can use the pattern matching / 'inline' deconstruction and it looks more like F#. But I understand how it's more error prone.
i've been using this since you introduced it a long time ago, It's an amazing package
What about using alias instead of creating a new class with inheritance and source generator?
Unions in TS and value enums in Rust are such great features that theyre the only reason I don't use c# for basically everything. If c# had them natively, Itd be basically perfect
I love the concept and use similar solutions in newer projects whenever I can.
Literally need this feature I just learned about just now.
This is something I adore in Typescript. Thanks for the video.
I come from Dart and there are actually a couple of packages tackling the functional approach. Have been using Options and Either, pattern matching seems to be added soon. I see the big advantage in a cleaner control flow, no exceptions flying around to be catches etc. still a beginner in c#, this looks kind of familiar👍 Nice explanation, thks
I effectively implemented exactly that pattern for something at work, but the use of implicit operators was considered a bit too magic, and went against the company value of "simple is better than clever" too much. Now I have an explicit ResultOrError.Success(T) or ResultOrError.Fail(Errors) sort of thing.
BTW: R# and Rider can show inline type info stuff, and that includes a little icon wherever an implicit operator is used.
PS: Check out R# and Rider's extended colours - colouring things by their semantic meaning, so things like const, enum value, parameter, local mutable var, local singly-assigned var can all be different colours; similarly Class vs Static Class vs Struct vs Record etc.
I didn't know about that source generator, nice! It would be even better if it could generate a struct like the OneOf from the package
Love it! Will need to try it. Keep up the great vids!
i use a base controller class with control flow in the basic crud endpoints, validate (fluent), map (automapper), send to service (usually works for non crud endpoints too).
maybe not the best possible way but it's clean and easy to follow.
this seems less obfuscated, certainly, but at the cost of adding a lot of 'noise' imo.
I think this is great - I like self-describing code, and I think this goes a long way towards that - I also strongly dislike error-handling for expected conditions, and I feel this makes the code a lot more dry
Source generators seem to be a very underappreciated power tool for avoiding boilerplate
Yeah that was my biggest "hey I should look into this" takeaway from this video too :D
I think more authors need to make it the norm in their libraries
I love it. I don’t love the refactoring when it’s part of the language that’s coming but I suppose you get that no matter what.
You'll have to wait at least 2-3 years for that so don't worry too much about it
Great video, thanks! Looking forward to giving this a try at some point!
What the video is missing is the comparison with the current best approach without external dependencies.
A common interface for possible results can often be used.
Damn, really good video, Nick! And awesome library.
I feel targeted 🤣
It's like you recorded this just for me after our discussion on null vs Empty collections the other day - I like it.
👍
I'd love to see result.Match replaced with a contract mapping that decides the result code based on either an underlying interface, or static mapping to some of the OneOf types.
Also interested in the Swagger/OpenAPI support for this - definitely will have a play around with this package - thanks for sharing!
I am torn on this one. On the one hand i like the descriptiveness of the method signatures. But on the other hand i worry about readability. This adds error checking and mapping code everywhere while with exceptions you just dont see it. Also how composable is a codebase when all methods can return unions, how do you deal with errors comming from a few layers away? I kinda like exceptions, they dont get in the way
That is exactly the problem that DU's solve, you DON'T see the places it's likely to break. He gave a really good example of it I think, where from the outside of that function it was impossible to tell with certainty that it actually was going to behave in 3 ways, when it really looks like it can only behave in 2.
Also he pointed out that that returned bool was not really very definitive either, it was an indication of the truth or falsehood of... Hopefully the whole thing being successful, but simply to succeed or fail is lacking context entirely.
There is only usually one way for something to succeed, and very typically many ways to fail, and if you don't have a good idea of why you might have failed, then your program simply cannot be expected to behave in a way that isn't accounted for.
You're completely right in that there is more visually to digest, but in a CSharp situation it's not really much of a choice for there not to be, unless you simply don't care about the programming api to be as clear as possible about it's underlying complexity.
And that's the core of the problem, what's being described here is an issue of inherent complexity, there is no avoiding the fact that by the nature of the request being made, it has 3 behaviors at minimum. To change that it would have to be broken up somehow, and it might not be feasible to break it up in some situations.
If you care quite a bit about readable code that still completely expresses the domain behavior, (and you care to entertain recommendations that you didn't ask for, lol) I highly recommend looking at using FSharp. The solution he's using an external library for in CSharp is part of the basic structure of the language in FSharp. It's a first class citizen there, and it makes so much of a difference to have the language built around the feature you're using. I recommend the fantastic beginner course by Matthew Crews on the FastFSharp channel. Also www.fsharpforfunandprofit.com is killer.
I guess I am the only one who would prefer to write DUs by hand. Just create a struct with private constructor, turn all the value constructors of the case classes to public factory methods, and you are 80% done.. Now you have your own type with no dependency on an external package.
A very useful nuget package and well explained. Thanks alot
This also make sure developers don't miss handling any return value and this is enforced compile time which is neat
And code becomes explicit as well because there is no magic that Dev has to discover as to where does this exception is handled
I love the concept, it feels like F#. I would love to organize my code like that. However not sure how I feel about using that while it's not built-in.
Sounds a little bit like `Validation` of LanguageExt, which either returns a valid object or a list of errors.
I am not a fan of the match or switch method that require to declare behaviour in the same order than the declared generics. It makes for a terse syntax but it , somewhat, couple the implementation with the consumer.
I can see the apeal though. I will have to test it out to see if it is really a problem or if i'm just grumpy this morning :D
I did not know of this techniques, thank you!
Thank you for the insightful video
Hello, Nick!
Are you able to use this with a Validation Pipeline in mediator? In a way so we don't need to repeat in our services every method that needs validation or something.
Also, can we use something like a result filter so we don't need to always use match for all our controllers actions? Thanks
Nice! Interested to see what OneOf looks like in Swagger?
I wouldn’t know since I use produces attributes. I don’t leave my swagger docs left to what the framework manages to infer automatically
THis is deadly! thank you, man!
This makes c# more functional
This looks like the UnionAttribute of c# Language-ext library and then you can use pattern matching with switch expression.
Use a struct Carrying statuscode and object?
Do a switch on the statuscode in controller
my next project is definetely using this
Can't you define a result class with status enumeration field for example, then return it and inside the controller map them to response?!
Sure you can but that doesn’t make it a good approach. Having one type per result and having the user to explicitly handle, alongside all the relevant type information is a better approach
Yes, the generic approach is more suitable to avoid defining classes for each, thanks Nick
That awesome1 I wonder will work with pattern matching since it has a implicit convert?
How this works with autorest? How your swagger documentation could work with this to return one or another?
what is slug?
I wonder if you have looked at the ErrorOr package from Amichai Mantinband Nick? I wonder how it compares.
Does this library supports default match case?
Nice one. Out of this video, can you please create a video on how to use fluent validations as attribute at the top of the class if possible?
Thanks for this video.
Great stuff. Thank you
Is it possibe to use this with async?
Ah ah, I thought the validation middleware was pretty cool, but I see your point :)
Can you inherit oneof to support many http responses and only need to use one generic parameter? So like HttpOf?
OneOf is a struct.
When would people suggest to use One of vs languageext with the result type?
The Result type is just one of the possible things too can do with OneOf. LanguageExt is great if you are planning to use things like Result, Option, Union etc. not just result
@@nickchapsas So use the Result in conjunction with OneOf. I was just a bit confused as it seemed the main use case of result is handled with OneOf and matching, as you replace exceptions with types/validation, though I could see if getting a specific value or an operation causes an exception Result still would be useful along with OneOf.
Nice work
I have tried to use this package some time ago but it did not handle serialization and deserialization. I ended up implementing a similar class that can do serialization. My context is Azure Durable Functions where serialization is heavily used when returning from Activity Functions. I hope that the official implementation will be able to handle serialization correctly.
For many situations I've found the simple approach of returning a tuple indicating (a) success/failure and (b) relevant object upon success ... works well. Sort of a poor man's version of this video. Since you only look at (b) if (a) is true, doesn't matter much what the default value of (b) is in case of failure.
That's a really nice feature, I'm currently working on framework 4.8 and there is no nullable so that could be a nice alternative
Can we use something similar as Result match for Validation Result?
Sure but when you have 3 or 4 possible return types then Result is not enough
@@nickchapsas I mean success or error, i do not like that if:)
Waiting for c# to add it to the language with syntactic sugar.. I just hope it's gonna be just the right amount of sugar and the right type. Wonder how much time it will take to add this simple concept
Since switching to Python, I am getting unable to program whithout discriminated unions. I've been advocating that languages basically converge now, and here we have yet another example of convergence between C# and Python.
it's not directly related to the topic here but I've got a question to anyone who'd be interested: if you make a web service call in a method, would you handle the exception within the method for 500 or network failure and populate a failed result which is defined in return type, or would you not handle and expect it to be handled somewhere else?
My exception handling middleware catches the exception, logs it, and returns a 500 error.
Great video Nick, as always! The OneOf library doesn't seem to play very nicely with return types of IEnumerable though. Is this an issue with my design, should i wrap IEnumerables in a container model? Seems a bit of fudge to get it to work with this library. I currently use CSharpfunctionalExtensions but like the idea of being able to return more than 2 result types.
I really want someday C# will copy the "inline" of Kotlin, it would be huge and absolutely amazing for things like OneOf and dunet, because it will reduce greatly the overhead of calling these lambdas instead just placing the code right there
What is the overall “cost” of the switch statement and in your opinion when do things go a bit bat shit crazy with switch statements of 10+ possible results lol, it seems to “shortcut” right to the right place but is this an expensive operation or actually highly efficient switching?
A much needed feature
Will not be the same using a Enum instead of OneOf? Which are the advantages of using OneOf instead of Enums?
I think you would only have to answer the question of how that would be able to express the return of a movie or not found? Or how would you return a detailed error or a success?
That's the advantage of OneOf.
@@tomheijtink8688 you return the enum type and then in the parent method you receive the type and check with an if, switch or whatever the type and then the endpoint return the class, will be mostly the same than OneOf, I dont see a really true advantage with OneOf aproach
I'd love to use that in our company, but I don't think it will get through 😓
@Nick Chapsas feature is really great, but what you forgot to mention is that this library don`t work with interfaces.
I`ve also cheked ValueOf lib.
Any solutions to do the same with interfaces?
for the Ienumerable I can call .ToList().
But is it possible to do it without ToList?
Results?
I'm weighing the pros and cons of using this versus a monad pattern. Any thoughts?
They are orthogonal, you can have a monad over a DU
OneOf seems useful. When you get to Match, I wonder if it will be too obscure in the future when new guy tries to maintain the code.
Quite the opposite. You have clear return paths with clear responsibilities. If anything it makes it easier
I really like the idea of discriminated unions, the service looks more cleaner without exceptions being thrown, etc. But what I don't like is a Match method with a set of lambdas in the controller, this looks somelike ugly. I would prefer the following JS-like syntax more: var (movie, notFound, validationError) = await service.UpdateAsync(); and then you check if you have a movie - return Ok(), if not found - return NotFound(), otherwise BadRequest(). Again it's not ideal but for me it looks better than a set of lambdas. Btw, Nick have you checked the performance when using OneOf comparing to versions that can return multiple values in one class but without throwing exceptions ? How much overhead do the lambdas have?
IMHO OneOf is excellent to represent alternative types but it is less beneficial to model a processing pipeline (with failures). In these cases a Result monad is more expressive and its composability is a major value. Something like Result should be more effective.
What happened to IActionResult, it could return anything...is it not the same thing you are trying to achieve here..?
No
Awesome!
I like this a lot but i would rather see this implemented on the client side. That way i can have a distinct expected switch for each api call. Oneof movie,notfound,validation,exception so i can do similar to a promise and have reusable handlers. Essentially a quick use switch because switch doesnt understand multiple return types.
Hi Nick,
I am interested to buy your complete course bundle.
may I know if there is any discount code for that?
The bundle is already discounted by 20%. There isn’t a further discount
I prefer using Result pattern 😉
You are using Discriminated Unions then
Maybe they will Add monads in future for chaining this into pipes
I think the phrase I was waiting for is "Mutually Exclusive".
Does the OneOf package actually also improve the performance of the API in this case?
If you come from thowing exceptions and catching them for validation, then yes, significantly.