With the vast amount of the BCL that was null annotated in net5 and if you treat warnings as errors (which you should) then yeah. NRT is now the a fitting, and more idiomatic c# solution.
@@Luturol You could mark it as non Nullable reference type and throw null exception in the method, outside the method you will be 100% sure it can't be null, ergo, there is no need to check fo nulls, there is no boilerplate code.
An important topic right there. Null values in a langauge where the type system has no notion of "nullability", null values are a liability that you have to work around, e.g. vai Optionals. In a language which has the concept of a nullable type built right into the type system (such as Kotlin or TypeScript), null values become an asset. The compiler of such a language will actually refuse to compile a file by throwing a compile error at you if you try to do anything that *might* result in a NullPointerException. This has been revolutionary for my team and for my way of writing code. This feature alone makes Kotlin vastly superior to Java. It may seem like a small thing, but the impact it has is tremendous.
I like this pattern and we use it a lot. Otherwise an interface does not signal whether you should check the result for null or not - you have to read the documentation, or the implementation. Nullable referense types also solves that problem
Languages without null, like Rust, use exactly the same Opiton thingy. It does 3 things: - explicitly removes null from places it doesn't have to be (or would ever exist) - makes sure programmer is aware / handles the null case - makes programmer write more code Other than that, nothing changes. So generally it's safer code at cost of more code. I like these videos, they're pretty expert and unopinionated (professional). Thanks for doing this. I would love to see some material on possibilities of making sure a function won't change what you pass to it (like not passing mut in rust or using const & in c++).
Good for you doing a video on this Nick, I come from a Kotlin environment where null safety is a first class citizen and I was blown away how much of a game changer it was. IMO the C# world has been slow to adopt the trick properly. I will say C# 9 nullability syntax with warnings as errors is what we use for this now, skipped C# 8 cause it had awful collisions with nullable struct syntax and generics. If that’s not an option something similar can be achieved with ReSharper/Rider, JetBrains Annotations and Implicit Nullability if running static analysis checks is viable. Short of that though this is what I’d use, good job.
It’s one of the reasons why I love Kotlin so much. With C# it’s obviously an afterthought and it’s up to the user to adopt which is understandable but sad if it doesn’t get adopted widely
Tony Hoare has said "null was my billion dollar mistake" but I don't think he was right about that sentiment.The Programming World needed a Universal Type that reflects empty, has no members words in a simple way so we came Up with Nullable so that way any Type can be nullable the class looks for if the passed object is initialized or not and if initialized does it have a value set to it if yes then it is not null. So it was the Best way to do it really. Besides the whole .Net Framework Technologies depends on the Nullable Type there is no easy way of changing things now. I don't even mention the zillion of programs all around the World that is on production heavily depends on Nullable Type...
There are no safe operations on null, though. That is the difference. The world does need some Empty or Bottom type. That should not be a type whose default behaviour is to go kaboom, though. It's a type whose default behaviour should be a no-op, with a reminder to deal with the no-op, *without* encouraging people to just swallow catches, or return null to the next poor sap who needs to do the exact same things you just did... possibly without the same level of concern for soundness of the logical flow of the application.
I love options, but I've had a hell of a time convincing my coworkers of their value. Occasionally I'll have some beautiful, elegant piece of code as a result of using them and they'll appreciate it, but never enough to try them for themselves. I've started sneaking options into common libraries to force it though :)
I think this is very subjective but a neat solution. Personally I like to use vanilla paradigma and since you have to rewrite all the interfaces and functions to use "Option" you end up with a "Get(...)" and a "Match(...)" function instead of your "null" check. In this specific case I could make the code "null" aware in other ways e.g. by implementing a "bool TryGet(..., out)" instead. But at this point I guess it is just a matter of taste.
I prefer the try-pattern with bool TryGetSomething(out SomeType result). I n this solution the decision making is also implied and I don't need any unnecessary lambdas.
But if you try to actually access the returned optional object, you sooner or later still have to do a check whether the object is valid or "none", so essentially it did not solve the null check problem, you just shoved the validity check to another place and did it in a more fancy way.
If the only way to access the object is by "Match" then effectively you've compiler enforced the check, since you can't call Match for the Some only case, you MUST provide a None case or it will fail compilation. It moves the error from runtime to compile time. you can also do this post C# 8 by enabling Nullable reference types and treating all warnings as errors.
The point is not to remove the necessity of checking null values but to force the caller of a function to acknowledge the fact that it can return an empty value. This is mostly a safety/readability feature.
@@r.c8756 I read what you're saying but all I hear in my head is 'People are lazy in reading and writing documentation and have very dangerous assumptions.' 🤣 But seriously, developers that want to use this have had ways of doing this since day 1 of C# (me in part). Developers that don't want to use this won't (me in another part, bit of internally (protected) split on this issue). 😉
@@rolandtennapel5058 it's not about being lazy, missing a null check is a common bug even for seasoned developers. Having tools to help you keep your code sound is never a bad thing, and the possibility of trading a runtime error for a compile time error is always a plus. Java has Optional types as well and they are very useful.
@@rolandtennapel5058 Documentation will never replace code quality and yes, people are lazy. They sometimes make mistakes and sometimes, they even plainly suck. This is kind of a negative way of looking at things but on the other hand, making things safe is thinking of the worse that can happen and then think of a way to prevent it to happen. As a programmer, you should always assume that the next guys who’s gonna use your code will mess up in the worse possible way. (and if you have a bit of experience in the field, you know that this happen more often than you’d hope...). So an important part of our job is to write code in a way that they can’t.
Null is not an issue, it is the lack of self documentation in the function signature public MyClass GetTheThing(); This function can return an instance, a null or throw an exception but you have no idea looking at the sig. This can mean people do not handle the possible outcomes. This is actually the correct signature such that just looking you can determine what can come back. public Either GetTheThing(); C# 8 with nullable reference rates part of the pain out as you know if its nullable. The more info devs have on what can come back the less risk of blow up when in production :)
Full Disclosure posting before watching video, but my initial reaction is that it isn't as long as that behavior is expected from whatever's returning null, as while I completely agree that it isn't necessarily optimal or desirable per se, I've seen it used to make variables effectively ternary values when null is used, and it worked very well, but it was similarly documented well enough that the behavior was expected. I'll update this after the video if my opinion changes. Thank you so much for explicitly touching on that there *WAS* indeed a way with Unsafe code though. Burying heads in the sand around those possibilities doesn't make them go away, but many have done it from my experience.
It would be so cool to see a round table discussion between you, Zoran Horvat, and someone like Eric Lippert or Anders Hejlsberg discuss the merits of nullable types vs the Optional pattern in C#. There seems to be some degree of dogmatism around it and it would be nice to see that stripped away.
I agree that nulls are a multibillion dollar mistake due to finding null reference exceptions in the Production environment sometimes for edge case scenarios and other times deep within a property chain which takes some extra time to debug and resolve. The use of the generic Option class isn't the elegant solution I was expecting. I was expecting Options to be built into the language and complier itself and include extra option functions and syntax
You need to see some code for this to make sense. I have had discussions about this on SESE and they went nowhere. It only gets meaningful at 9:45. Before that one can just go "So it won't blow up at the same point, it will continue to run in an undetermined state which is worse! You will have to check at some point, whether it be for null or None...". Obviously for fluent interfaces null is literally a show stopper, I got that. Now I also see the returned wrapper object forces you to obtain the (encapsulated) value in a safe way. This is the real boon that often gets lost in a focus on null itself.
I use it on UI layer and Features layers (CQRS). Nulls and Exceptions are reserved to Infrastructure layer. Everything up must use Option like class type of response to force my devs to handle all cases on the application domain and the UI.
Yeah C# was basically trying to get to Kotlin's implementation with C# 8 but the problem is that null worked like this for so many years that it's been engraved to anyone's muscle memory and it's so hard to change your mind on something that you've been doing for years.
Spend twice as much time testing than you do coding and it won't matter. Either you coded for null or you didn't and if you didn't it's a bug that you must fix.
@@MoMadNU Why waste time testing things the compiler can trivially verify for you? Test time is better than runtime, but compile time is better still. (Not to mention the more explicit/correct API)
@@berylliosis5250 The compiler cannot detect a null reference exception which represents 90% of the exceptions you will encounter because of bugs in your code.
Good stuff, I think functional programming patterns are great stuff that leads to cleaner and more expressive code, with less chance for bugs. C# is becoming more functional with every release. Would love to see more content in that direction on how to setup/utilize constructs like different monads and circuit breaker patterns.
I love functional programming, I programmed for more than one year in Scala then when I came back to C# 4 years ago I found that there weren't so many functional features built-in the language itself. But now the language it is envolving and that's a good thing
wow... someone never saw C# colesce operators... outch... guess that hurts now^^ return customer ?? Notfound(); and this can be valid syntax because if your method is an ActionResult you can directly return T and it will be converted into an OK
I am a C programmer and watch these videos for fun... ...and in each of these I get the feeling that these modern language standards are trying to solve problems that are not problems in the first place... ....aand most of the times the solutions are worse. :)
👆👆 yep. what’s worse about this is these cultural forces (not the beautiful language itself) ends up ruining the language in practice (because to use it you have to work in environments with such enforced paradigms), so then we all have to go back to C to escape this!
My coding standard insisted that a function only has one return path. Multiple return paths can be confusing, especially if it is code you inherited from someone else who has left the company.
'Should you stop returning "null"? ' Hell no. The mere question signifies overthinking. In regards to objects, trying to avoid null, which is there for a legitimate and useful reason, usually leads to over-programming if not bad programming. Lists are another matter. Those should always be initialized and have a count of 0 if empty.
I agree, the code is much less readable in this case. I’m all for optional values (like std::optional) but I wouldn’t use it for nullables because a nullable already has the functionality built in, ergo the ability to check for a null reference.
I really don’t understand this disposition of mind. Questioning practices is the most elementary step of learning and improving and you never stop doing that in a career. With that mindset you can never improve... In only looks less readable because it challenges your habits.
@@r.c8756 It's good to question things and consider new approaches, but you're stepping backwards if you add unneeded complexity. Less readable is less readable; that has nothing to do with habits being challenged. A software engineer, especially a contractor, can't be successful for any considerable number of years without challenging their habits, getting outside their comfort zones, and expanding their knowledge on a consistent basis. Not returning null and engineering a work around to avoid it is none of those.
The thing I like about the approach, personally, is that the design tells you that you have to account for "none." Nullable reference types are good for that too.
@@logank.70 Exactly re nullable ref types, so why go to the trouble of the design? Just seems to be a case of overthinking and recreating the wheel simply because the programmer doesn't want to use nullable refs already included in the framework. Besides, nullables are something every programmer should already know how to handle, even a junior one.
Update: I discovered the IfSome method which is exactly what I need for my use case. So no unsafe! I like this approach! I do have a use case question though. In my repository layer which uses EntityFramework, some columns are nullable and so if a property is null, I need to return a null value. I can do this using MatchUnsafe, but I'd prefer not to use it unless I have no other choice. Is there a better way to do this? Example: ExternalUserId is a string property in an EF table that is nullable in the database. ExternalUserId = user.ExternalUserId.Match(e => e, (string)null); This throws an exception when returning null. ExternalUserId = user.ExternalUserId.MatchUnsafe(e => e, (string)null); This works, but I'd prefer not to use it. Thoughts?
Not for me, but I get it and I appreciate it 😏 I guess I'm just too comfortable with assuming anything / everything can be NULL and check for it (or pass by reference which helps a lot).
@Nick Chapsas - I would be curious to how you feel about this style in 2022. I've unconsciously implemented something very simular in my codebase and really like this style personally.
I like this video, and it's a cool package; but my two cents are that NRT's were enough for most scenarios back when the video was released, and with the upcoming .NET 7, auto-implemented null checks will make it even more so. This further adds to boilerplate in recent versions, but if you're stuck with legacy, this package could be a god-sent
Is there any inefficiencies introduced to using Option? You say it's a struct but there must be somewhere where a reference or a no-reference is indicated that doesn't occur with null and not null. The code is also more verbose than traditional use of null and compiler helping with Object? vs Object helps identify and remove bugs during dev. Not convienced yet but interested in your further forays into Functional Programming. Thanks Nick.
For an API i would never use null return value. I'd return and object with some kind of warning message with 400 or 500 status code. So the recipient understands what happened. Also, the response is going to be handled differently because the status code is not OK. Inside assembly i think it is okay to use null. The key is to be consistent which is harder to reach working as a team.
Hi Nick, about your api route I've been having issues using guid type as a parameter (invalid guids return oks), but as I saw in your example you parse it in the controller. Is this the best way to do this?
I do like this (and I use F# a lot, so this makes a lot of sense), however, I think with the newer C# nullable `Customer?` I don't think it is needed. The language will force us to handle the case where the value is null. If we are handling a nullable, or we are handling an Option we are still handling it. I also like the idea of `map` and `bind` functions but we can also create those for Nullables just as easily as we can create them for Options.
It will only force you if you enable nullable reference types as errors in your project and even then you can ignore if you if think you know better than the compiler with the ! operator
@@nickchapsas Yes, that ! is true. I do that 😝 But usually, only in cases I know would have had an error a long time ago if what I am accessing is null. Maybe it proves your point. I have never seen the language extension library you talk about. I might check it out, although like I had said I use a lot of F#, so I might have a method returning FSharpOption or FSharpResult. This library probably wouldn't work on those.
I prefer to use functional syntax, its a lot more readable, though its harder to understand what's going on inside. But I'm wondering, how much overhead does Option usage have? especially .Match call with lambda-parameters
Very good question. It's my bad that I didn't cover it during the video. On this specific example: The Non-Option approach will execute at a mean time of 146 nanoseconds and will allocate 168 bytes of memory (it's probably the Guid object allocation). The Option approach will execute at a mean time of 222 nanoseconds and will allocate 296 bytes of memory (it's the Guid plus the Option object, even though this is stack memory allocation due to Option being a struct)
With the latest C#, if enabled, there is nullable reference types similar to int?. With this all reference types by default are not nullable which can mitigate much of this out and the time implications will be reduced. When you use nullable reference types you have the same .HasValue and .Value properties along with operators like ?? and .? Personally I have come around to null is evil. I have lost count of the null exception errors I have chased down over the decades I have done at the code face. It is far better to use this sort of technique to force devs to have to deal with the full types of the return value instead of mask it. If you get an Option or MyClass? returned you know you have the chance you have no value and are forced to handle it. If you just get MyClass returned without that protection you have no idea. It makes the function more explicit. Take the following public Result DoTheThing(); You can instantly see you might get a "good response" that might have a value or you get an error. No need to dig into the code to see if it can throw etc
I prefer always return IEnumerable even when just one object is expected. That solves the "returning null" issue. When no object found you will get empty collection.
Option is basically just a list of 0 or 1. Some implementations of option actually just extend IEnumerable and essentially just provide convenience methods for returning a list of 1 or 0
It works but it’s a (dirty) hack which damages the readability of your code. The very point of typing is to add meaning to otherwise completely abstract and decontextualized algorithms.
@@aborisov20 Returning an IEnumerable tells whoever read your code that they should expect several elements. This is deceitful and therefore, bad practice. Correct use of types, variable and method name should describe as accurately as possible their behavior... What do you disagree with exactly ?
I would say yes but I don't code in C#. I always return some sort of "None" type reference. Does not matter what language I code in. For a strong typed returns like Java, I would subclass the return type to identify value like NullMap for returning a Map that is "None". Never done C# though.
You'd be incorrect. That's not what null is. The type system does not give us a way of representing "nothing" or "no value". So we use null to do that, even though it's problematic. And since everybody has treated it that way for so long, the compiler team had to create work-arounds so people would stop hurting themselves with it.
the only way i use null atm is if i got an method that i want too have 3 values where 1 might be null cause i only wanna use two, i prefer makeing an class where i can return a bool and a message(string) back and do stuff with that. so even if the return is null it means the bool check for if it´s true so if it returns as null it would run the "Failed" code and just say " it failed".
If we have to operate on an object we're getting from DB, we still have to check for null before using it (In service or in the controller). So how does it benefit? I will still try it and see If it helps.
Thanks for the video . why don't we add that option just before the return statement where we needed as I don't like the idea of changing the return type everywhere. If I want to share the logic between the module , how that works as it don't expose the object as it is.i need use some method in the option struct to get the retuning object value .it's unnecessary overload and performance issue as well.
Maybe this example doesn’t illustrate the point as well as I’d like it to because it’s a very simple example. Realistically you would have different return types on each layer and you wouldn’t just be able to return option itself. The whole point of option is that you cannot get the value without handling the None scenario and that needs to happen across your application. Also, like I said in a different comment, performance impact is negligible.
The options semantics is a bit obtuse. The exact same behavior is achievable in a clearer manner. The interface method should be changed from: Option Get(GUID id) ; To: bool TryGet(GUID id, out Customer customer); The first one has obfuscation that hurts more than it helps. TryGet signatures are clear and automatically enforced by proxy of out parameters requiring assignment by the compiler.
I don't like this if I have it in the interfaces, because it makes the nuget a requirement for implementing them, and sometimes that is not allowed. So yeah, nice to remove "if (x!=null)" and convert it into something more explicit, as long as it does not have repercussions down the chain.
2 ปีที่แล้ว +1
I prefer not to return null when possible. I also try to avoid nullable value types.
The only difference between returning null or to use "maybe", is that the latter gives you compiler assistance to enforce you to write that if/else statement that you would otherwise forget. Personally I think it's overengineering to use such a construct. If/else works just fine if you are disciplined enough and test your code before deployment.
Great video! But it's not almost the same on using c#8 Nullable reference types? I don't know how c#8 nullable exactly works, I think only throws a warning when compiling but actually it compiles. In that case, I agree with the use of Option. If we could make that the code does not compile, It would be right to use the nullable type right?
The thing with Nullable reference types is that it doesn't really prevent you from handling them. You can still use the bang operator (!) to suppress it and it's not an error until you explicitly mark it as one. It's basically an afterthought and realistically, people won't instantly jump into using it because they can get away without using it. Option is something that you need to acknowledge no matter what, which i prefer.
@@nickchapsas this is a very interesting discussion and maybe worth picking up in another video showing the approaches side by side with a more complex example. I'm currently leaning towards nullable reference types because I have seen code with pretty big classes and handling the matches made it quite difficult to read and understand. And with a good culture where you try to eliminate all the warnings I think its nice for a lot of cases. Also I just remembered that you can install the FxCop nuget package and have the null reference warnings pop up as errors instead so the project does not compile.. I probably should try that
You can configure the code to not compile if there are such warnings, and if one use ! forging operator, well, what he's saying is that "ok, I know it can be null but I don't care" which doesn't mean he forgot about null-check, pretty much the opposite. I think using yet another library to handle a case that's already done by the language is just too much. It was useful before c#8 but now it's just more complexity
Hi, considering that C# has nullable types which if used can lead to eliminating null pointer exceptions as well, do you think that Option still has a place in there?
I do actually and I see them as a different thing. Nullable types and the compiler can lie and Jon Skeet has a great talk about that. It’s also to the library author’s discretion to use them. Option beats all that with a clean forced matching scenario
The best way to handle it is using exceptions. If you expect a value and you get null -> that's an exception to handle. Trying to work around it is just not worth it. Exception is explicit, caught in the code, easy to test, you can write your own and make it easy to find, you can return as many different exceptions as you wish at any time and add more as codebase grows. It's just too good to use anything else.
Exceptions should be exceptional. I am not a fan of taking a performance hit just to have an easy way out of the application's flow. It's convinient, sure, but it's by no means good.
You do a simple IF in first code... But now, you generate a struct that consume memory and it's not a reference type, so, the object is copied in all return, plus you need two lambdas that's generate a lot of process and context keep process.... Why this is good ??? You need more memory, more process, more processor stack, and so on....
Because your code is safer. The benchmarks show a performance degradation in the class of a few nanoseconds for the benefit of NRE elimination and for predictable code. Ofc it’s something you can ship if you commit to using null reference types properly but again that’s up to the dev
@@nickchapsas if you put a check for null. Where the dangerous path of that ? Plus this nanosecond with a Friday, science software, bi software, financial software... And of course, change all your code... It's the basic of a language null treatment.... It's like... We are using a stable old fashion jeans... Let's create a radical new one.
@@ericfelipe2011 The dangerous part is that it's up to the developer to put the null check in. What you're assuming is that every nullable thing will be null checked at the right place, which is simply not true.
@@nickchapsas with new coalesce it's even simpler Return object?.property ?? New list() With so many code analítics, it's can be configured yo show a warning of a null reference or something.
@@ericfelipe2011 Coalesce is a completely different feature and has nothing to do with this. I agree that Null reference types can be configured to give a similar experience but it's still up to the developers and the team to enable it and make it mandatory (error instead of warning)
I just get the feeling that if it is "none" then it has the form of the object, but could be uninitialized, and could kind of be more dangerous. if the code attempts to access an uninitialized field then it could lead to bad results, but if it tries to access null then it throws an exception. is it possible for a none to be modified into a real version of the struct/object. I just get the feeling that if it can be modified at any point then bad out of bounds data could cause bigger problems and maybe volnerabilities rather then the program hitting an Exception.nullReferenceException
These containers aren't for low-level operations, and aren't for async values (that last part isn't exactly true... *this* container isn't for async values... async/await itself hides the concept of containerizing requests), they're for code composition. At any point in your example, some programmer could have your instance mutated by 8 different threads at the same time, with no thread locks; how is null going to help you in that case? If that's how your team writes code, then nothing can save you. The class is already there and uninitialized, or poorly reconfigured. If my code needs to return an A, but in order to make an A, I need an x which I have, and a y which I don't, I can solve that in ways better than passing around uninitialized instances. async GetA () { x = GetLocalX(); potentialY = await GetY(); potentialA = potentialY.Map(y => new A(x, y), None); return potentialA; } I am only going to make an A in the case that I know I can correctly construct that instance. And what the next user gets is something that they can operate on (through Mapping from A -> B), Filtering from (A -> None) in the case they aren't interested in that instance, handling the case where they got nothing, or just passing it on to the next person down the line, who will do any or all of the above, before themselves passing it on. It benefits the system in general, to write it in such a way that you have no incomplete or ill-configured objects/structs/sets/etc floating around, regardless of what architecture you're using for code composition.
"None" does not have the form of the class you are trying to access. Lets say you have a method that returns an object of type Option. If you call that method, and it happens to return Some, you can think of that as wrapping a Person object. If you want to use that object, you kinda have to reach in and get it. In other words, you can't just treat the Option object like it's a Person object. On the other hand, if you call that method and it happens to return None, there is no Person object to access. In reality, that Option is probably wrapping a null Person object. But that's just an implementation detail, you have no way of accessing it. You can't accidentally access it. People in these comments seem to think the point is to have a clever way around null checking. That's not it. The point is to have a real representation of "no value" without bringing null into the equation at all.
Hey Uzair, I really appreciate the comment. This is exactly why I make these videos. It doesn't need to be something that you will use but it's good to know that there is also this thing here that might be of use.
@@nickchapsas indeed exploring different options are definitely worth the time you never know when you might need that different approach. Keep it up 👍
I am looking for this too. I personally prefer using c# nullable between repository and services layers. But I think that option may serve better between services and controllers layers. It is great, Thanks for sharing.
1. What that code will look like when there will be two separate repository calls? Let's say get two different customers and return Ok only when both exists. 2. That library seems are over complicating things. If you need that Match method - write simple extensions method and check if value is not null call ok otherwise fail.
@@nickchapsas you're referring to the second delegate? Uhh true i guess. Although you can pass null to that one as well but then you did it intentionally while null awareness can be forgotten. Yeah good point, got it.
Thanks nice simple demo. I really like Option, however now C#8 has Nullable Reference Types there is less of a need for it. And as brilliant and extensive as the LanguageExt library is, it encourages C# developers to go deeper into using other Functional concepts which could easily confuse other team members and create unreadable code. Hopefully one day Option is built into C# and then we can freely use it, otherwise there is an argument that if you want to do real functional programming then you should stick to a Functional language like F#. I think this is valid, but it also frustrates me as there are not many F# jobs out there.
I prefer to never return null and always provide a default value. In this case the default value is a real instance of a customer with the name "Not Found".
It is a big mistake to think that replacing null with custom value gives you anything valuable. You will not avoid exceptions, or even worse, you will get weird behavior and one more elusive bug in the tracker instead of a simple exception pointing you to exact number of the string where it happened in the code. Plus, with Nuget package like this one, you will get worse performance because there is additional structure requiring additional memory and comparison with custom value of None instead of hardware optimized comparison with null.
Based on my personal experience in the past 2 years, it’s very much the opposite actually. It has completely removed any NREs, the code it way more predictable and deterministic and the performance degradation is so slim it’s not even a factor. Not only that but since these types are structs there is minimal memory footprint changes.
It was a big mistake to make the nullability (and omnipresent `default`) concept core to the language. Could you provide an example of weird behavior with Option? Besides nulls being converted to None.
While it seems like generally a good idea, it's simply too late? C# has null so deeply rooted that it has ridiculous amount of internal null checks where even if something is not supposed to have nulls (like this Option structure), it will be still internally routed through dozen of null checks for half of the actions...
I think it is a nice feature but I don't think I'll use anything like this unless it is part of .NET. I think having dependencies like this is a bit problematic. Doesn't nullable reference types basically solve the same problem anyways? You are telling the developer and the compiler that this can be null and thus should be checked (fortunately, Rider and VS also tell you this). (I get that some developers aren't comfortable with moving to a nullable project since it is a bit difficult to transition to but it is there).
I am ok with null, it make the code less readable because not every body uses the library. I like to stick to language features as close as I can it make my code simple.
Well, that’s the downside of using that kind of functionality. This kind of decision should probably be best taken and enforced as a team and not just as a personal initiative
The only issue with Option is it's not well-behaving: given `string F(bool b) => b ? "" : null; bool G(string s) => s == "";`, `false.ToSome().Map(F).Map(G)` must be equal to `false.ToSome().Map(v => F(G(v)))`, but it's not
Doesn't this pattern fail as soon as you're trying to call methods of the customer object when you actually got none? I'd always prefer the NullObject pattern: Create an Interface ICustomer, that's implemented by both Customer and a NoCustomer class, the latter implementing the expected behaviour for when you don't have a customer. This way all your other code doesn't need to care anymore whether they actually found a Customer or not.
If you want to use it on another class you'd have to again create an interface for it and the null class, which would force you to use the interface everywhere. That might not be possible without some big refactoring. Additionally, it won't work if it's a class from a library.
@@zenitkov Yes, I would always do that: Wherever I can have "zero or more" of, I would use the NullObject Pattern. It saves me from checking for null everywhere and have other code actually know, that there could be none of something. For a library: I would always create a Wrapper for the use of a library anyways. Within that wrapper I could translate null into my own NullObject.
The Option doesn't have a customer in the "None" case, just a marker struct meaning "No value was found". There is only a customer object in the "Some" case. Which makes it 100 % safe to access the None part. It's much like a checked exception or an out parameter. But unlike checked Exceptions, Option has the semantic meaning of "this method can return None, and it's not very unexpected." The null object pattern is good too, but the Option approach is more explicit and requires far less work than implementing a null object.
I don't see the need to complicate the code with these fancy thing. In the service layer, it is still necessary to check whether the object was found. And this library will not provide any benefits. And even more, if you received NRE, then something is wrong with your code, anyway you need to fix that (with or without NRE) and you will notice it obviously. Perhaps this library will make the code a little "prettier" in the controllers, but if you haven't found the entity, you may need additional logic and "Option" construction will only disfigure your code
first you saying that we need specific handling of null case but then you do same, you specifically handling null case but it will be converted to null and has different syntax don't see really a big difference to be honest maybe on bigger example it will make sense
Sounds like an overly complicated way of returning something other than null when the value your about to return is null... but c# already does this: return customer ?? NotFound; another thing is your returning JSON back to the caller, so I get the idea of option but what the caller gets back isn’t going to be strongly typed... am i missing something? Besides if your getting null reference exceptions, it just an example of a poorly coded application that lacks error handling...
Null coalescing has nothing to do with Option's null handling. Sure it's a fallback but it's not different than an if == null check that can be omitted if the writer isn't careful. It doesn't force you to handle it. Option forces you to acknowledge the fact that this value can be null and demands you to handle it somehow. There is no way around it. It's basically the null reference type feature of C# 8 before C# 8 became a thing. I don't think that NREs can be a metric to determin whether an application is poorly coded or not. If the value can be null then you should be forced to acknowledge it. Whether that is via the Null Refernece Types of C#8 or Option, it doens't matter.
@@nickchapsas i apologize, your right, my statement regarding NRE’s was a generalization... but isn’t the use of option just a way of saying if object A is null, return object B... Isn’t null coalescing doing the same thing? I do like the idea of I’m returning a customer object of null that maybe had a boolean flag of CustomersFound set to false rather than null. That to me makes sense...
@@ajcroteau0928 So you're right, the outcome is the same and in fact there will also be a small performance hit due to how the Option struct works. The difference is that Option doesn't give you the option (no pun intended) to not ackowledge the potential of a null. Basically you could write the same code without Option but using it guarantees, no matter what, that the developer has absolutely no way to mess it up by not handling the null option. It forces you to write more robust code.
Not sure I like this too much. I get the idea but let's say it's for newer developers. This is the kind of hack that may make their life easier but doesn't teach them good basis, it actually hides their flaws. Most 3rd party libraries still heavily rely on null. For example, the quite common newtonsoft JSON. Also if you don't learn the good practices around object manipulation and have to learn let's say JS. If you do not have the good habit of checking objects before using them, it's not going to be a fun day. In the end, I don't think this is helpful as it won't help developers improve and will just hide their lack of good practices. The value I do see in using Option is maybe proof of concept? So your demo doesn't crash? But then, what is the benefit vs a TryGet method with a nullable? It seems like a bool TryGetCustomer(Guid id, out Customer? customer) achieves a similar result?
The thing is, you can't expect someone to have good habits. Option enforces good habits. Users (new or experienced) using an API you may have witten will know the value might be null.
The TryGet method you propose DOES achieve a very similar result. The question is, would you prefer that over Option? If so, go ahead and use the TryGet! But according to your own logic (since the TryGet and the Option are so similar), the TryGet doesn't enforce good habits and hinders developers' improvement.
This is wrong. You cannot not handle None when you’re using Option. The code won’t compile. This is the difference, that you NEED to handle None or else the code won’t even build. There are no NREs
If you're getting null reference exceptions, it's because you're not bothering to check for them in situations where they are a possible, and valid, return result. That's a programmer problem.
I think it’s much easier to just use C# nullability syntax.
Well, quality of code sure has a cost...
With the vast amount of the BCL that was null annotated in net5 and if you treat warnings as errors (which you should) then yeah. NRT is now the a fitting, and more idiomatic c# solution.
even tho u have to check if has value in some cases
@@Luturol You could mark it as non Nullable reference type and throw null exception in the method, outside the method you will be 100% sure it can't be null, ergo, there is no need to check fo nulls, there is no boilerplate code.
Exactly! Not much easier, it's way cleaner to the code.
An important topic right there. Null values in a langauge where the type system has no notion of "nullability", null values are a liability that you have to work around, e.g. vai Optionals. In a language which has the concept of a nullable type built right into the type system (such as Kotlin or TypeScript), null values become an asset. The compiler of such a language will actually refuse to compile a file by throwing a compile error at you if you try to do anything that *might* result in a NullPointerException. This has been revolutionary for my team and for my way of writing code. This feature alone makes Kotlin vastly superior to Java. It may seem like a small thing, but the impact it has is tremendous.
I like this pattern and we use it a lot. Otherwise an interface does not signal whether you should check the result for null or not - you have to read the documentation, or the implementation. Nullable referense types also solves that problem
Languages without null, like Rust, use exactly the same Opiton thingy. It does 3 things:
- explicitly removes null from places it doesn't have to be (or would ever exist)
- makes sure programmer is aware / handles the null case
- makes programmer write more code
Other than that, nothing changes. So generally it's safer code at cost of more code.
I like these videos, they're pretty expert and unopinionated (professional). Thanks for doing this. I would love to see some material on possibilities of making sure a function won't change what you pass to it (like not passing mut in rust or using const & in c++).
I remember missing an equivalent of Maybe in Haskell years ago, but have since then forgotten why. Thanks for refreshing my memory!
Good for you doing a video on this Nick, I come from a Kotlin environment where null safety is a first class citizen and I was blown away how much of a game changer it was. IMO the C# world has been slow to adopt the trick properly.
I will say C# 9 nullability syntax with warnings as errors is what we use for this now, skipped C# 8 cause it had awful collisions with nullable struct syntax and generics. If that’s not an option something similar can be achieved with ReSharper/Rider, JetBrains Annotations and Implicit Nullability if running static analysis checks is viable.
Short of that though this is what I’d use, good job.
It’s one of the reasons why I love Kotlin so much. With C# it’s obviously an afterthought and it’s up to the user to adopt which is understandable but sad if it doesn’t get adopted widely
Hi Nick, would really appreciate it if you could increase your font size a bit. Really helps out the mobile viewer.
Tony Hoare has said "null was my billion dollar mistake" but I don't think he was right about that sentiment.The Programming World needed a Universal Type that reflects empty, has no members words in a simple way so we came Up with Nullable so that way any Type can be nullable the class looks for if the passed object is initialized or not and if initialized does it have a value set to it if yes then it is not null. So it was the Best way to do it really. Besides the whole .Net Framework Technologies depends on the Nullable Type there is no easy way of changing things now. I don't even mention the zillion of programs all around the World that is on production heavily depends on Nullable Type...
There are no safe operations on null, though. That is the difference. The world does need some Empty or Bottom type. That should not be a type whose default behaviour is to go kaboom, though. It's a type whose default behaviour should be a no-op, with a reminder to deal with the no-op, *without* encouraging people to just swallow catches, or return null to the next poor sap who needs to do the exact same things you just did... possibly without the same level of concern for soundness of the logical flow of the application.
I love options, but I've had a hell of a time convincing my coworkers of their value. Occasionally I'll have some beautiful, elegant piece of code as a result of using them and they'll appreciate it, but never enough to try them for themselves. I've started sneaking options into common libraries to force it though :)
I think this is very subjective but a neat solution.
Personally I like to use vanilla paradigma and since you have to rewrite all the interfaces and functions to use "Option" you end up with a "Get(...)" and a "Match(...)" function instead of your "null" check.
In this specific case I could make the code "null" aware in other ways e.g. by implementing a "bool TryGet(..., out)" instead.
But at this point I guess it is just a matter of taste.
I prefer the try-pattern with bool TryGetSomething(out SomeType result). I n this solution the decision making is also implied and I don't need any unnecessary lambdas.
But if you try to actually access the returned optional object, you sooner or later still have to do a check whether the object is valid or "none", so essentially it did not solve the null check problem, you just shoved the validity check to another place and did it in a more fancy way.
If the only way to access the object is by "Match" then effectively you've compiler enforced the check, since you can't call Match for the Some only case, you MUST provide a None case or it will fail compilation.
It moves the error from runtime to compile time. you can also do this post C# 8 by enabling Nullable reference types and treating all warnings as errors.
The point is not to remove the necessity of checking null values but to force the caller of a function to acknowledge the fact that it can return an empty value. This is mostly a safety/readability feature.
@@r.c8756 I read what you're saying but all I hear in my head is 'People are lazy in reading and writing documentation and have very dangerous assumptions.' 🤣 But seriously, developers that want to use this have had ways of doing this since day 1 of C# (me in part). Developers that don't want to use this won't (me in another part, bit of internally (protected) split on this issue). 😉
@@rolandtennapel5058 it's not about being lazy, missing a null check is a common bug even for seasoned developers. Having tools to help you keep your code sound is never a bad thing, and the possibility of trading a runtime error for a compile time error is always a plus. Java has Optional types as well and they are very useful.
@@rolandtennapel5058 Documentation will never replace code quality and yes, people are lazy. They sometimes make mistakes and sometimes, they even plainly suck. This is kind of a negative way of looking at things but on the other hand, making things safe is thinking of the worse that can happen and then think of a way to prevent it to happen. As a programmer, you should always assume that the next guys who’s gonna use your code will mess up in the worse possible way. (and if you have a bit of experience in the field, you know that this happen more often than you’d hope...). So an important part of our job is to write code in a way that they can’t.
Null is not an issue, it is the lack of self documentation in the function signature
public MyClass GetTheThing();
This function can return an instance, a null or throw an exception but you have no idea looking at the sig. This can mean people do not handle the possible outcomes.
This is actually the correct signature such that just looking you can determine what can come back.
public Either GetTheThing();
C# 8 with nullable reference rates part of the pain out as you know if its nullable. The more info devs have on what can come back the less risk of blow up when in production :)
Full Disclosure posting before watching video, but my initial reaction is that it isn't as long as that behavior is expected from whatever's returning null, as while I completely agree that it isn't necessarily optimal or desirable per se, I've seen it used to make variables effectively ternary values when null is used, and it worked very well, but it was similarly documented well enough that the behavior was expected. I'll update this after the video if my opinion changes. Thank you so much for explicitly touching on that there *WAS* indeed a way with Unsafe code though. Burying heads in the sand around those possibilities doesn't make them go away, but many have done it from my experience.
It would be so cool to see a round table discussion between you, Zoran Horvat, and someone like Eric Lippert or Anders Hejlsberg discuss the merits of nullable types vs the Optional pattern in C#. There seems to be some degree of dogmatism around it and it would be nice to see that stripped away.
I agree that nulls are a multibillion dollar mistake due to finding null reference exceptions in the Production environment sometimes for edge case scenarios and other times deep within a property chain which takes some extra time to debug and resolve. The use of the generic Option class isn't the elegant solution I was expecting. I was expecting Options to be built into the language and complier itself and include extra option functions and syntax
That was one of the first things I noticed when I played around with F#. Option is built-in.
Cool video, Please continue with this functional approach it's great!!!
You need to see some code for this to make sense. I have had discussions about this on SESE and they went nowhere. It only gets meaningful at 9:45. Before that one can just go "So it won't blow up at the same point, it will continue to run in an undetermined state which is worse! You will have to check at some point, whether it be for null or None...". Obviously for fluent interfaces null is literally a show stopper, I got that. Now I also see the returned wrapper object forces you to obtain the (encapsulated) value in a safe way. This is the real boon that often gets lost in a focus on null itself.
I use it on UI layer and Features layers (CQRS). Nulls and Exceptions are reserved to Infrastructure layer. Everything up must use Option like class type of response to force my devs to handle all cases on the application domain and the UI.
I prefer nullability. And I love how null safety implemented in kotlin
Yeah C# was basically trying to get to Kotlin's implementation with C# 8 but the problem is that null worked like this for so many years that it's been engraved to anyone's muscle memory and it's so hard to change your mind on something that you've been doing for years.
I really like the idea. It prevents exceptions in the code, also leting new programmers doing less errors;
In that case - better way to prevent new programmers from doing errors is not allow them to code. :D
@@amuuuinjured LoL
Spend twice as much time testing than you do coding and it won't matter. Either you coded for null or you didn't and if you didn't it's a bug that you must fix.
@@MoMadNU Why waste time testing things the compiler can trivially verify for you? Test time is better than runtime, but compile time is better still. (Not to mention the more explicit/correct API)
@@berylliosis5250 The compiler cannot detect a null reference exception which represents 90% of the exceptions you will encounter because of bugs in your code.
Good stuff, I think functional programming patterns are great stuff that leads to cleaner and more expressive code, with less chance for bugs.
C# is becoming more functional with every release. Would love to see more content in that direction on how to setup/utilize constructs like different monads and circuit breaker patterns.
I really like this approach. Wish I implemented it on a project since dealing with NULL’s was a huge pain for my app.
I like the idea. Prevents exceptions and it feels like a proper approach. I would definitely enjoy more videos like this. Thank you!
I still love use and return null :)
But, from an execution perspective, which method is better on memory and execution speed?
I love functional programming, I programmed for more than one year in Scala then when I came back to C# 4 years ago I found that there weren't so many functional features built-in the language itself. But now the language it is envolving and that's a good thing
at the end of the day, it depends on how the value is treated. it also has some benefits that developers can use with it.
wow... someone never saw C# colesce operators... outch... guess that hurts now^^
return customer ?? Notfound();
and this can be valid syntax because if your method is an ActionResult you can directly return T and it will be converted into an OK
I am a C programmer and watch these videos for fun... ...and in each of these I get the feeling that these modern language standards are trying to solve problems that are not problems in the first place... ....aand most of the times the solutions are worse. :)
👆👆 yep. what’s worse about this is these cultural forces (not the beautiful language itself) ends up ruining the language in practice (because to use it you have to work in environments with such enforced paradigms), so then we all have to go back to C to escape this!
My coding standard insisted that a function only has one return path. Multiple return paths can be confusing, especially if it is code you inherited from someone else who has left the company.
'Should you stop returning "null"? ' Hell no. The mere question signifies overthinking. In regards to objects, trying to avoid null, which is there for a legitimate and useful reason, usually leads to over-programming if not bad programming. Lists are another matter. Those should always be initialized and have a count of 0 if empty.
I agree, the code is much less readable in this case. I’m all for optional values (like std::optional) but I wouldn’t use it for nullables because a nullable already has the functionality built in, ergo the ability to check for a null reference.
I really don’t understand this disposition of mind. Questioning practices is the most elementary step of learning and improving and you never stop doing that in a career. With that mindset you can never improve... In only looks less readable because it challenges your habits.
@@r.c8756 It's good to question things and consider new approaches, but you're stepping backwards if you add unneeded complexity. Less readable is less readable; that has nothing to do with habits being challenged. A software engineer, especially a contractor, can't be successful for any considerable number of years without challenging their habits, getting outside their comfort zones, and expanding their knowledge on a consistent basis. Not returning null and engineering a work around to avoid it is none of those.
The thing I like about the approach, personally, is that the design tells you that you have to account for "none." Nullable reference types are good for that too.
@@logank.70 Exactly re nullable ref types, so why go to the trouble of the design? Just seems to be a case of overthinking and recreating the wheel simply because the programmer doesn't want to use nullable refs already included in the framework. Besides, nullables are something every programmer should already know how to handle, even a junior one.
Use null if needed. Check for null. Or use not null option.
Update: I discovered the IfSome method which is exactly what I need for my use case. So no unsafe!
I like this approach! I do have a use case question though. In my repository layer which uses EntityFramework, some columns are nullable and so if a property is null, I need to return a null value. I can do this using MatchUnsafe, but I'd prefer not to use it unless I have no other choice. Is there a better way to do this?
Example:
ExternalUserId is a string property in an EF table that is nullable in the database.
ExternalUserId = user.ExternalUserId.Match(e => e, (string)null);
This throws an exception when returning null.
ExternalUserId = user.ExternalUserId.MatchUnsafe(e => e, (string)null);
This works, but I'd prefer not to use it.
Thoughts?
Not for me, but I get it and I appreciate it 😏 I guess I'm just too comfortable with assuming anything / everything can be NULL and check for it (or pass by reference which helps a lot).
And its simpler to check if null... In thus case we need to use additional library and still write some checking code
@@willembeltman Checking inside the function means you don't have to remember to check outside the function. 😉
@Nick Chapsas - I would be curious to how you feel about this style in 2022. I've unconsciously implemented something very simular in my codebase and really like this style personally.
Great video! Would be great for generating object model audit trail. Keep up the awesome work!
Any plans to do Kubernetes, Swarm, rancher?
I like this video, and it's a cool package; but my two cents are that NRT's were enough for most scenarios back when the video was released, and with the upcoming .NET 7, auto-implemented null checks will make it even more so. This further adds to boilerplate in recent versions, but if you're stuck with legacy, this package could be a god-sent
Is there any inefficiencies introduced to using Option? You say it's a struct but there must be somewhere where a reference or a no-reference is indicated that doesn't occur with null and not null. The code is also more verbose than traditional use of null and compiler helping with Object? vs Object helps identify and remove bugs during dev. Not convienced yet but interested in your further forays into Functional Programming. Thanks Nick.
For an API i would never use null return value. I'd return and object with some kind of warning message with 400 or 500 status code. So the recipient understands what happened. Also, the response is going to be handled differently because the status code is not OK. Inside assembly i think it is okay to use null. The key is to be consistent which is harder to reach working as a team.
Thanks Nick for another insightful video
Great video! I was always wondering if C# had a similar to optional from Java
Hi Nick, about your api route I've been having issues using guid type as a parameter (invalid guids return oks), but as I saw in your example you parse it in the controller.
Is this the best way to do this?
I do like this (and I use F# a lot, so this makes a lot of sense), however, I think with the newer C# nullable `Customer?` I don't think it is needed.
The language will force us to handle the case where the value is null.
If we are handling a nullable, or we are handling an Option we are still handling it.
I also like the idea of `map` and `bind` functions but we can also create those for Nullables just as easily as we can create them for Options.
It will only force you if you enable nullable reference types as errors in your project and even then you can ignore if you if think you know better than the compiler with the ! operator
@@nickchapsas Yes, that ! is true. I do that 😝
But usually, only in cases I know would have had an error a long time ago if what I am accessing is null.
Maybe it proves your point.
I have never seen the language extension library you talk about.
I might check it out, although like I had said I use a lot of F#, so I might have a method returning FSharpOption or FSharpResult.
This library probably wouldn't work on those.
I prefer to use functional syntax, its a lot more readable, though its harder to understand what's going on inside.
But I'm wondering, how much overhead does Option usage have? especially .Match call with lambda-parameters
Very good question. It's my bad that I didn't cover it during the video.
On this specific example:
The Non-Option approach will execute at a mean time of 146 nanoseconds and will allocate 168 bytes of memory (it's probably the Guid object allocation).
The Option approach will execute at a mean time of 222 nanoseconds and will allocate 296 bytes of memory (it's the Guid plus the Option object, even though this is stack memory allocation due to Option being a struct)
With the latest C#, if enabled, there is nullable reference types similar to int?. With this all reference types by default are not nullable which can mitigate much of this out and the time implications will be reduced.
When you use nullable reference types you have the same .HasValue and .Value properties along with operators like ?? and .?
Personally I have come around to null is evil. I have lost count of the null exception errors I have chased down over the decades I have done at the code face. It is far better to use this sort of technique to force devs to have to deal with the full types of the return value instead of mask it. If you get an Option or MyClass? returned you know you have the chance you have no value and are forced to handle it. If you just get MyClass returned without that protection you have no idea. It makes the function more explicit.
Take the following
public Result DoTheThing();
You can instantly see you might get a "good response" that might have a value or you get an error. No need to dig into the code to see if it can throw etc
Since new null checking feature in dotnet, which can be set to an error, I think the issue with returning null is (pun intended) "null and void".
I prefer always return IEnumerable even when just one object is expected. That solves the "returning null" issue. When no object found you will get empty collection.
Option is basically just a list of 0 or 1. Some implementations of option actually just extend IEnumerable and essentially just provide convenience methods for returning a list of 1 or 0
It works but it’s a (dirty) hack which damages the readability of your code. The very point of typing is to add meaning to otherwise completely abstract and decontextualized algorithms.
@@r.c8756 Disagree with the first sentence and agree with second sentence. :)
@@aborisov20 Returning an IEnumerable tells whoever read your code that they should expect several elements. This is deceitful and therefore, bad practice.
Correct use of types, variable and method name should describe as accurately as possible their behavior... What do you disagree with exactly ?
@@r.c8756 Nothing related to term "dirty hack". Just expected behaviour. By design.
Null for life!!! lol but this is quite nice. I'll check it out. Looks like the optional interface in Java, which I've used a lot
Should you stop returning null? Yes. Replace them with Optional.
I would say yes but I don't code in C#. I always return some sort of "None" type reference. Does not matter what language I code in. For a strong typed returns like Java, I would subclass the return type to identify value like NullMap for returning a Map that is "None". Never done C# though.
But whats wrong with null check? Why do we need to complicate code for a simple thing?
In any case we should check if null or if match
You can avoid null check, but cannot avoid match
"not null, just the idea of no value." Pretty sure that's what null is.
@Ishaan Jaxtyn Insane! It took me 5 minutes to say it worked!
You'd be incorrect. That's not what null is. The type system does not give us a way of representing "nothing" or "no value". So we use null to do that, even though it's problematic. And since everybody has treated it that way for so long, the compiler team had to create work-arounds so people would stop hurting themselves with it.
the only way i use null atm is if i got an method that i want too have 3 values where 1 might be null cause i only wanna use two, i prefer makeing an class where i can return a bool and a message(string) back and do stuff with that. so even if the return is null it means the bool check for if it´s true so if it returns as null it would run the "Failed" code and just say " it failed".
If we have to operate on an object we're getting from DB, we still have to check for null before using it (In service or in the controller). So how does it benefit? I will still try it and see If it helps.
Thanks for the video . why don't we add that option just before the return statement where we needed as I don't like the idea of changing the return type everywhere. If I want to share the logic between the module , how that works as it don't expose the object as it is.i need use some method in the option struct to get the retuning object value .it's unnecessary overload and performance issue as well.
Maybe this example doesn’t illustrate the point as well as I’d like it to because it’s a very simple example. Realistically you would have different return types on each layer and you wouldn’t just be able to return option itself. The whole point of option is that you cannot get the value without handling the None scenario and that needs to happen across your application. Also, like I said in a different comment, performance impact is negligible.
@@nickchapsas thanks, it would be great if you have another video which covers the said point.
@@nickchapsas "performance impact is negligible." Famous last words before performance issues commmence ;)
@@airjuri I can guarantee with 100% confidence that this will be the last thing that you will need to optimize in your application
The options semantics is a bit obtuse.
The exact same behavior is achievable in a clearer manner.
The interface method should be changed from:
Option Get(GUID id) ;
To:
bool TryGet(GUID id, out Customer customer);
The first one has obfuscation that hurts more than it helps. TryGet signatures are clear and automatically enforced by proxy of out parameters requiring assignment by the compiler.
brilliant, its clean way to return from Action Method
I don't like this if I have it in the interfaces, because it makes the nuget a requirement for implementing them, and sometimes that is not allowed.
So yeah, nice to remove "if (x!=null)" and convert it into something more explicit, as long as it does not have repercussions down the chain.
I prefer not to return null when possible. I also try to avoid nullable value types.
The only difference between returning null or to use "maybe", is that the latter gives you compiler assistance to enforce you to write that if/else statement that you would otherwise forget. Personally I think it's overengineering to use such a construct. If/else works just fine if you are disciplined enough and test your code before deployment.
2:35
Flag 'found' is redundant. You can just:
_customers.TryGetValue(customerId, out var customer);
return customer;
It would be nice to see a video using Option with async method
It would work in the exact same way. You'd just have to return Task.
Great video! But it's not almost the same on using c#8 Nullable reference types? I don't know how c#8 nullable exactly works, I think only throws a warning when compiling but actually it compiles. In that case, I agree with the use of Option. If we could make that the code does not compile, It would be right to use the nullable type right?
The thing with Nullable reference types is that it doesn't really prevent you from handling them. You can still use the bang operator (!) to suppress it and it's not an error until you explicitly mark it as one. It's basically an afterthought and realistically, people won't instantly jump into using it because they can get away without using it. Option is something that you need to acknowledge no matter what, which i prefer.
@@nickchapsas this is a very interesting discussion and maybe worth picking up in another video showing the approaches side by side with a more complex example. I'm currently leaning towards nullable reference types because I have seen code with pretty big classes and handling the matches made it quite difficult to read and understand. And with a good culture where you try to eliminate all the warnings I think its nice for a lot of cases. Also I just remembered that you can install the FxCop nuget package and have the null reference warnings pop up as errors instead so the project does not compile.. I probably should try that
You can configure the code to not compile if there are such warnings, and if one use ! forging operator, well, what he's saying is that "ok, I know it can be null but I don't care" which doesn't mean he forgot about null-check, pretty much the opposite.
I think using yet another library to handle a case that's already done by the language is just too much. It was useful before c#8 but now it's just more complexity
Great Chanel and tutorials. Thank you for sharing your knowledge. Keep going!
Perfect! Thanks for this video
Hi, considering that C# has nullable types which if used can lead to eliminating null pointer exceptions as well, do you think that Option still has a place in there?
I do actually and I see them as a different thing. Nullable types and the compiler can lie and Jon Skeet has a great talk about that. It’s also to the library author’s discretion to use them. Option beats all that with a clean forced matching scenario
The best way to handle it is using exceptions. If you expect a value and you get null -> that's an exception to handle. Trying to work around it is just not worth it. Exception is explicit, caught in the code, easy to test, you can write your own and make it easy to find, you can return as many different exceptions as you wish at any time and add more as codebase grows. It's just too good to use anything else.
Exceptions should be exceptional. I am not a fan of taking a performance hit just to have an easy way out of the application's flow. It's convinient, sure, but it's by no means good.
You do a simple IF in first code...
But now, you generate a struct that consume memory and it's not a reference type, so, the object is copied in all return, plus you need two lambdas that's generate a lot of process and context keep process....
Why this is good ???
You need more memory, more process, more processor stack, and so on....
Because your code is safer. The benchmarks show a performance degradation in the class of a few nanoseconds for the benefit of NRE elimination and for predictable code. Ofc it’s something you can ship if you commit to using null reference types properly but again that’s up to the dev
@@nickchapsas if you put a check for null. Where the dangerous path of that ?
Plus this nanosecond with a Friday, science software, bi software, financial software...
And of course, change all your code...
It's the basic of a language null treatment....
It's like... We are using a stable old fashion jeans... Let's create a radical new one.
@@ericfelipe2011 The dangerous part is that it's up to the developer to put the null check in. What you're assuming is that every nullable thing will be null checked at the right place, which is simply not true.
@@nickchapsas with new coalesce it's even simpler
Return object?.property ?? New list()
With so many code analítics, it's can be configured yo show a warning of a null reference or something.
@@ericfelipe2011 Coalesce is a completely different feature and has nothing to do with this. I agree that Null reference types can be configured to give a similar experience but it's still up to the developers and the team to enable it and make it mandatory (error instead of warning)
I just get the feeling that if it is "none" then it has the form of the object, but could be uninitialized, and could kind of be more dangerous. if the code attempts to access an uninitialized field then it could lead to bad results, but if it tries to access null then it throws an exception.
is it possible for a none to be modified into a real version of the struct/object. I just get the feeling that if it can be modified at any point then bad out of bounds data could cause bigger problems and maybe volnerabilities rather then the program hitting an Exception.nullReferenceException
These containers aren't for low-level operations, and aren't for async values (that last part isn't exactly true... *this* container isn't for async values... async/await itself hides the concept of containerizing requests), they're for code composition.
At any point in your example, some programmer could have your instance mutated by 8 different threads at the same time, with no thread locks; how is null going to help you in that case? If that's how your team writes code, then nothing can save you. The class is already there and uninitialized, or poorly reconfigured. If my code needs to return an A, but in order to make an A, I need an x which I have, and a y which I don't, I can solve that in ways better than passing around uninitialized instances.
async GetA () {
x = GetLocalX();
potentialY = await GetY();
potentialA = potentialY.Map(y => new A(x, y), None);
return potentialA;
}
I am only going to make an A in the case that I know I can correctly construct that instance. And what the next user gets is something that they can operate on (through Mapping from A -> B), Filtering from (A -> None) in the case they aren't interested in that instance, handling the case where they got nothing, or just passing it on to the next person down the line, who will do any or all of the above, before themselves passing it on. It benefits the system in general, to write it in such a way that you have no incomplete or ill-configured objects/structs/sets/etc floating around, regardless of what architecture you're using for code composition.
"None" does not have the form of the class you are trying to access.
Lets say you have a method that returns an object of type Option. If you call that method, and it happens to return Some, you can think of that as wrapping a Person object. If you want to use that object, you kinda have to reach in and get it. In other words, you can't just treat the Option object like it's a Person object.
On the other hand, if you call that method and it happens to return None, there is no Person object to access. In reality, that Option is probably wrapping a null Person object. But that's just an implementation detail, you have no way of accessing it. You can't accidentally access it.
People in these comments seem to think the point is to have a clever way around null checking. That's not it.
The point is to have a real representation of "no value" without bringing null into the equation at all.
What about throwing a custom exception and catching it at an upper level ?
Wow, that's awsome solution.
I appreciate the tutorial.
I don't like the syntax that much, I prefer C# nullable over this.
Of course this is my own opinion.
Hey Uzair, I really appreciate the comment. This is exactly why I make these videos. It doesn't need to be something that you will use but it's good to know that there is also this thing here that might be of use.
@@nickchapsas indeed exploring different options are definitely worth the time you never know when you might need that different approach.
Keep it up 👍
I am looking for this too. I personally prefer using c# nullable between repository and services layers. But I think that option may serve better between services and controllers layers.
It is great, Thanks for sharing.
1. What that code will look like when there will be two separate repository calls? Let's say get two different customers and return Ok only when both exists.
2. That library seems are over complicating things. If you need that Match method - write simple extensions method and check if value is not null call ok otherwise fail.
How about using Null Object Pattern to avoid returning null?
Whats so different between null awareness or none awareness?
null awareness is optional and can be skipped. Option is a discriminated union and it has to be handled no matter what. It's safer
@@nickchapsas you're referring to the second delegate? Uhh true i guess. Although you can pass null to that one as well but then you did it intentionally while null awareness can be forgotten. Yeah good point, got it.
Hey nick can you please make a video on web api concurrency issues with an example .
Thanks nice simple demo. I really like Option, however now C#8 has Nullable Reference Types there is less of a need for it. And as brilliant and extensive as the LanguageExt library is, it encourages C# developers to go deeper into using other Functional concepts which could easily confuse other team members and create unreadable code.
Hopefully one day Option is built into C# and then we can freely use it, otherwise there is an argument that if you want to do real functional programming then you should stick to a Functional language like F#. I think this is valid, but it also frustrates me as there are not many F# jobs out there.
I prefer to never return null and always provide a default value. In this case the default value is a real instance of a customer with the name "Not Found".
It is a big mistake to think that replacing null with custom value gives you anything valuable. You will not avoid exceptions, or even worse, you will get weird behavior and one more elusive bug in the tracker instead of a simple exception pointing you to exact number of the string where it happened in the code. Plus, with Nuget package like this one, you will get worse performance because there is additional structure requiring additional memory and comparison with custom value of None instead of hardware optimized comparison with null.
Based on my personal experience in the past 2 years, it’s very much the opposite actually. It has completely removed any NREs, the code it way more predictable and deterministic and the performance degradation is so slim it’s not even a factor. Not only that but since these types are structs there is minimal memory footprint changes.
It was a big mistake to make the nullability (and omnipresent `default`) concept core to the language. Could you provide an example of weird behavior with Option? Besides nulls being converted to None.
While it seems like generally a good idea, it's simply too late?
C# has null so deeply rooted that it has ridiculous amount of internal null checks where even if something is not supposed to have nulls (like this Option structure),
it will be still internally routed through dozen of null checks for half of the actions...
I think it is a nice feature but I don't think I'll use anything like this unless it is part of .NET. I think having dependencies like this is a bit problematic.
Doesn't nullable reference types basically solve the same problem anyways? You are telling the developer and the compiler that this can be null and thus should be checked (fortunately, Rider and VS also tell you this).
(I get that some developers aren't comfortable with moving to a nullable project since it is a bit difficult to transition to but it is there).
Thought this was a joke, turns out you got a point. Thanks
I am ok with null, it make the code less readable because not every body uses the library. I like to stick to language features as close as I can it make my code simple.
Well, that’s the downside of using that kind of functionality. This kind of decision should probably be best taken and enforced as a team and not just as a personal initiative
Cool stuff dude thanks
Thanks for video. Integrasiting.
I like the approach
mindblown from the elegance of this syntax. Will try to implement it at work.
Why not use the nullobject pattern? forcing you to create a "null" workflow for the object? That is much more inline with SOLID.
C# 9 switch expressions are a nice built-in implementation of Match.
I use switch expressions all the time now. C# 9 makes writing functional code in C# much easier.
The only issue with Option is it's not well-behaving: given `string F(bool b) => b ? "" : null; bool G(string s) => s == "";`, `false.ToSome().Map(F).Map(G)` must be equal to `false.ToSome().Map(v => F(G(v)))`, but it's not
Nice one!
Doesn't this pattern fail as soon as you're trying to call methods of the customer object when you actually got none?
I'd always prefer the NullObject pattern: Create an Interface ICustomer, that's implemented by both Customer and a NoCustomer class, the latter implementing the expected behaviour for when you don't have a customer. This way all your other code doesn't need to care anymore whether they actually found a Customer or not.
If you want to use it on another class you'd have to again create an interface for it and the null class, which would force you to use the interface everywhere. That might not be possible without some big refactoring. Additionally, it won't work if it's a class from a library.
@@zenitkov Yes, I would always do that: Wherever I can have "zero or more" of, I would use the NullObject Pattern. It saves me from checking for null everywhere and have other code actually know, that there could be none of something.
For a library: I would always create a Wrapper for the use of a library anyways. Within that wrapper I could translate null into my own NullObject.
The Option doesn't have a customer in the "None" case, just a marker struct meaning "No value was found". There is only a customer object in the "Some" case. Which makes it 100 % safe to access the None part. It's much like a checked exception or an out parameter. But unlike checked Exceptions, Option has the semantic meaning of "this method can return None, and it's not very unexpected." The null object pattern is good too, but the Option approach is more explicit and requires far less work than implementing a null object.
I don't see the need to complicate the code with these fancy thing.
In the service layer, it is still necessary to check whether the object was found. And this library will not provide any benefits. And even more, if you received NRE, then something is wrong with your code, anyway you need to fix that (with or without NRE) and you will notice it obviously.
Perhaps this library will make the code a little "prettier" in the controllers, but if you haven't found the entity, you may need additional logic and "Option" construction will only disfigure your code
first you saying that we need specific handling of null case but then you do same, you specifically handling null case but it will be converted to null and has different syntax don't see really a big difference to be honest maybe on bigger example it will make sense
One is optional one is mandatory. That's the difference.
My colleague refactored my code which returns Optional to return null 😢 My heart was broken
there are times where null is useful. If a user doesnt have a custom profile set, you can chose to hide options if there is no item
We should really all be writing f# for our application and web tiers.
Sounds like an overly complicated way of returning something other than null when the value your about to return is null... but c# already does this: return customer ?? NotFound; another thing is your returning JSON back to the caller, so I get the idea of option but what the caller gets back isn’t going to be strongly typed... am i missing something? Besides if your getting null reference exceptions, it just an example of a poorly coded application that lacks error handling...
Null coalescing has nothing to do with Option's null handling. Sure it's a fallback but it's not different than an if == null check that can be omitted if the writer isn't careful. It doesn't force you to handle it. Option forces you to acknowledge the fact that this value can be null and demands you to handle it somehow. There is no way around it. It's basically the null reference type feature of C# 8 before C# 8 became a thing. I don't think that NREs can be a metric to determin whether an application is poorly coded or not. If the value can be null then you should be forced to acknowledge it. Whether that is via the Null Refernece Types of C#8 or Option, it doens't matter.
@@nickchapsas i apologize, your right, my statement regarding NRE’s was a generalization... but isn’t the use of option just a way of saying if object A is null, return object B... Isn’t null coalescing doing the same thing? I do like the idea of I’m returning a customer object of null that maybe had a boolean flag of CustomersFound set to false rather than null. That to me makes sense...
@@ajcroteau0928 So you're right, the outcome is the same and in fact there will also be a small performance hit due to how the Option struct works. The difference is that Option doesn't give you the option (no pun intended) to not ackowledge the potential of a null. Basically you could write the same code without Option but using it guarantees, no matter what, that the developer has absolutely no way to mess it up by not handling the null option. It forces you to write more robust code.
Not sure I like this too much. I get the idea but let's say it's for newer developers. This is the kind of hack that may make their life easier but doesn't teach them good basis, it actually hides their flaws. Most 3rd party libraries still heavily rely on null. For example, the quite common newtonsoft JSON.
Also if you don't learn the good practices around object manipulation and have to learn let's say JS. If you do not have the good habit of checking objects before using them, it's not going to be a fun day.
In the end, I don't think this is helpful as it won't help developers improve and will just hide their lack of good practices.
The value I do see in using Option is maybe proof of concept? So your demo doesn't crash?
But then, what is the benefit vs a TryGet method with a nullable?
It seems like a bool TryGetCustomer(Guid id, out Customer? customer) achieves a similar result?
The thing is, you can't expect someone to have good habits. Option enforces good habits. Users (new or experienced) using an API you may have witten will know the value might be null.
The TryGet method you propose DOES achieve a very similar result. The question is, would you prefer that over Option? If so, go ahead and use the TryGet! But according to your own logic (since the TryGet and the Option are so similar), the TryGet doesn't enforce good habits and hinders developers' improvement.
Well, if you dont handle nothing as a return value, you get the Same error with Null. So i dont Unterstand Why you want to delete it
This is wrong. You cannot not handle None when you’re using Option. The code won’t compile. This is the difference, that you NEED to handle None or else the code won’t even build. There are no NREs
If you're getting null reference exceptions, it's because you're not bothering to check for them in situations where they are a possible, and valid, return result. That's a programmer problem.
Hi, what's the IDE?
JetBrains Rider