How to Avoid Null Reference Exceptions: Optional Objects in C#

  Рет қаралды 23,542

Zoran Horvat

Zoran Horvat

Күн бұрын

Пікірлер: 162
@zoran-horvat
@zoran-horvat Жыл бұрын
Become a patron and get access to source code and exclusive live streams: www.patreon.com/posts/null-conundrum-c-81382208 Send over the examples of your class designs that you wish me to review and, maybe, include in the future video on code redesign and refactoring. KZbin is aggressively deleting all links. If you wish to submit your repo for review, use this form instead: codinghelmet.com/go/code-review-request
@kristianaranda
@kristianaranda Жыл бұрын
Too bad there is no button to give 1000 likes at once. Thanks, Zoran, for your master classes.
@zoran-horvat
@zoran-horvat Жыл бұрын
Thanks!
@tdao9741
@tdao9741 Жыл бұрын
I like that your lecture is about C# but sounds like I'm listening to an audio book of The Lord of The Rings. Like your courses on Pluralsight, great content as usual.
@michapaucki6627
@michapaucki6627 Жыл бұрын
i found you yesterday. Im literally crying watching your videos! IT'S BEAUTIFUL!! THANKS FOR YOUR AMAZING JOB!
@jonnroc
@jonnroc Жыл бұрын
I've been skeptical of the Option type, that is until seeing this video. You've convinced me to establish an implementation of Option in my own code library. Thanks, Zoran.
@Gio2k
@Gio2k Жыл бұрын
One tip: Just use language-ext. If you are starting in functional programming, do not try to implement your own monads.
@tdao9741
@tdao9741 Жыл бұрын
This lecture is one that I occasionally revisited in the past weeks while dipping my toes in Haskell from the fundamentals knowing that I'll be a better programmer coding in the functional style. I initially encountered the difficulty understanding monad, but now thinking back I think the difficulty mainly came from how difficult virtually everybody says it is. You did a wonderful job explaining this beautiful and powerful concept. I smiled bigger and bigger after each revisit as I understood more and more and finally totally understood. Thank you!
@刘健-m2x
@刘健-m2x Жыл бұрын
这是非常好的视频,在中国这样的视频几乎没有(可能是我没找到),每天重复的开发功能,瞬间点亮了我。 谢谢
@aalasso2
@aalasso2 Жыл бұрын
Very nice video. The Pluralsight course sounds like a course I'd be interested in taking. I've had a subscription previously, but I didn't much like the platform overall. I wish the course were available as a standalone purchase.
@jeremyjinglebell2762
@jeremyjinglebell2762 7 ай бұрын
Incredibly interesting video. Made me run it several days each time 10 minutes before I got lost or was forced to seek another video or missing part for me to understand. I will have to schedule another few days to re-watch and hopefully I start to get it later. Not easy to absorb such a stuff in one shot. I discovered other videos from Zoran and definitely going to watch them. I have to stop them often, lol. I like it though. Great content.
@pblakez
@pblakez 3 ай бұрын
Oh great explanation on monad Option, Optional in my case moving to Optional in java along with Result Object (not in java :-( rolled own) has reduced code by thousands of lines in my projects and made most importantly more readable the classic problem with no last name is sorting by lastname so common but always an edge case solved with panache with map reduce
@rGunti
@rGunti Жыл бұрын
I built a similar `Option` for a similar thing, but I made it implement `IEnumerable` so it‘s compatible with Linq. Was quite nice.
@legittaco4440
@legittaco4440 4 ай бұрын
I don’t know about that… not all optional things are enumerable
@marcotroster8247
@marcotroster8247 3 ай бұрын
​@@legittaco4440A monad is a container of a container. You have a functor if you can call Select() on a container. If you can call SelectMany(), it's a monad. There's a very good conference talk on monads explaining this for Java with map() and flatMap().
@sinan720
@sinan720 Жыл бұрын
Why create a new type Option when you can just write an extension method for Nullable? Are you aware that "Reduce(T default)" already exists on nullable types, it is just named "GetValueOrDefault(T default)".... Also the examples you showed are a perfect case for pattern matching, which looks much more readable than chaining "Map" calls.
@zoran-horvat
@zoran-horvat Жыл бұрын
Option type is a monad, like Result type or a monad that handles I/O. It is possible to extend nullable reference types to bridge the gap to optional objects, but the same approach would fail in the very next step - monadic error handling. Regarding the GetValueOrDefault, it is defined on the Nullable struct which only applies to value types, and value types cover only a negligible portion of cases. Therefore, it does not apply to nullable types as a whole, namely the nullable reference types which are dominant in domain modeling.
@martinprohn2433
@martinprohn2433 6 ай бұрын
@zoran-horvat Can you explain what you mean by "would fail in the very next step"? What I did in my code is creating an generic extensions method for Map and I could use nullable references like monads. (I also tried around with a manual monad class. But pattern matching etc. is much easier with nullable references than with monads.) Your Example with Book labes would work almost the same: ```csharp record Person(string FirstName, string? LastName = null); record Book(string Title, Person? author = null); string GetLabel(Person person) = person .LastName .Map(lastName => $"{person.FirstName} {lastName}") ?? person.FirstName; string GetBookLabel(Book book) => book .Author .Map(GetLabel) .Map(author => $"{book.Title} by {author}") ?? book.Title; ``` So my Extension method is this: ```csharp [return: NotNullIfNotNull(nameof(defaultValue))] public static TOut? Map(this TIn? value, Func mapper, TOut? defaultValue = default) { if(value is null) { return defaultValue; } else { return mapper(value) ?? defaultValue; } } ``` I don’t see why that would fail in the very next step.
@ZubriQue
@ZubriQue Жыл бұрын
Nice video. I'm building a Web API controller and have to fight nulls a lot. The assignment says that some int/long values can be null, so it's even more a headache.
@zoran-horvat
@zoran-horvat Жыл бұрын
I can almost see it 😊
@seriyezh
@seriyezh Жыл бұрын
Thank you for a great video! I had this question in my mind for some time and I'm happy that you decided to cover this topic.
@zoran-horvat
@zoran-horvat Жыл бұрын
Thanks! I also plan to cover advanced topics with optional objects.
@Darebit2k1
@Darebit2k1 Жыл бұрын
Usas mucho la programación declarativa pero sería muy bueno que nos explicaras porque viola o no, la ley de Demeter? Excelente explicación, gracias por compartir tus conocimientos
@auronedgevicks7739
@auronedgevicks7739 10 ай бұрын
if you're wondering what the point of all this is.. it's because he doesn't want to do "if (x == null)" checks. So first they give us null reference with all it's confusing buts, and you can even roll your own with your private constructors or factories, or even take it to the next level with Monads. But ask yourself why not just check for null? it's a simple if statement. Well because Functional Programming demands that things exist, Functional Programming doesn't check for errors. Everything is just there so we can write beautiful expressions with the ugly bits hidden in Monads etc. There's nothing wrong with null checks if you're not writing functional code.
@zoran-horvat
@zoran-horvat 10 ай бұрын
Actually, null references have one drawback compared to references to objects - they don't reference an object. If you need any information to complete the operation, then sorry. The information got lost in the process. It is not the principle in functional programming to always have an object. It is the principle in programming in general, and so it applies to object-oriented programming as-is.
@chudchadanstud
@chudchadanstud 6 ай бұрын
Optionals exist in C++ and Rust. They exist so that you don't forget to check for null references and reassures the programmer that null references are accounted for, pretty much eliminating null reference errors all together. In all honesty there should be no nullable objects in C# or Java. It literally doesn't make sense since they're strongly typed and garbage collected.
@vincentvogelaar6015
@vincentvogelaar6015 2 ай бұрын
The monad is superior to null. It has nothing to do with the paradigms in the background! The monad allows the developer to transform a runtime error into a compile time error. Compile time error > run time error
@LuizADRMarques
@LuizADRMarques Жыл бұрын
Very interesting, as usual. I had your Functional C# PS course in my to-do list for a while, I will definitely check it out now.
@fredrickamoako
@fredrickamoako Жыл бұрын
Senior developers like this one who will push you to do your best. "I don't like that coding style" 😂💯
@redcrafterlppa303
@redcrafterlppa303 Жыл бұрын
14:30 I couldn't agree less to borrow your wording. Optional shouldn't be used as a storage object since it's designed to be short lived. If you know that an object will only live a few microseconds the compiler and the jit can make assumptions based on that. Java is currently working on big changes in the memory model them, when complete, allow you to constrict a class to help the compiler make those assumptions and optional is expected to get that treatment. Optional wrappers should be cleared as soon as possible since continuing operations in a function with a none variant is of no use in most cases. Also deeply nested optionals are always messy. To come back to your option monad. It does exactly what the c# operators do. Not being able to execute functions is just a wrong statement. You can call functions that expect nullable parameters and can be sure that they handle that correctly. If a function expect non nullable parameters you get a warning and should confirm non nullity before calling the function. In my opinion the explicit nullable types added to c# are totally enough to be a fully qualified as an option monad.
@zoran-horvat
@zoran-horvat Жыл бұрын
Before we extend the discussion, how do you know that the optional object is stored inside the outer object?
@redcrafterlppa303
@redcrafterlppa303 Жыл бұрын
@@zoran-horvat in my most favorite option implementation from rust. It's part of the type system. A comparable solution in java would be this: sealed interface Optional permits Some, None {} final class Some implements Optional { public final T val; } final class None implements Optional {} I searched and found out the concept of a sealed hierarchy doesn't exist in c# that's why I displayed the basic implementation in java. To answer your question rather the optional is empty or not would be determined by pattern matching and instanceof checks. To be clear the java jdk implementation is similar to your version relying on nullability to determine emptiness which makes the wrapper completely unusable for non nullable types. (why shouldn't an int be optional?) If you are curious the implementation in rust looks like this: enum Option { Some(T), None } Yeah the language is just built for variant types.
@zoran-horvat
@zoran-horvat Жыл бұрын
@@redcrafterlppa303 My implementation in C# is a value type, which is equivalent to Rust's struct implementation in every respect. Most notably, there is no difference (literally) in object's layout whether it contains a nullable field or a struct Option. And hence there is absolutely no reason to shy away from holding an option if need be. However, it is very important to note that the outer caller cannot tell whether the option is stored or calculated. Regarding the nullable references and tbe idea of functions receiving them, that would be a needless complication added as a burden to those functions. A function that knows what to do with an object, now must do two things out of nowhere. And repeat that code in every single function you make... That is precisely what monads are used for in practice, so to remove infrastructural code from the substantial one.
@redcrafterlppa303
@redcrafterlppa303 Жыл бұрын
@@zoran-horvat the version shown in the video is a reference type constrained to only contain refrence types. I don't know the object layout semantics of c# too well but in rust everything not explicitly marked as a reference is stored in place like ref structs in c# I think. If you Model the optional container as a value type that embeds itself in the containing object using the same or close to the same memory space as the content directly I agree with the statement that an optional can and should be used as the storage container as it then is a 0 cost abstraction. A refrence type option is always a costly thing as it is more likely to be on the heap the longer it lives. I think the biggest reason java suggests to use optional only as a return type is to make sure escape analysis can optimize the heap allocated optional away to just a stack value. About the part with the functions receiving optional/nullable parameters it's the functions choice. It basically says that the function makes this parameter "optional" and it's presence or absence is effecting the functions operation. If a function expects non null parameters the caller is required to ensure they are. Fun fact C#'s "string? " syntax is just syntactic sugar for the Nullable type which is equivalent to your option type only using operators instead of methods as it is a language feature instead of a library implementation.
@digitaldias
@digitaldias Жыл бұрын
Only works internally. Using this pattern on any kind of API will just give grief, i.e, for an endpoint that delivers books and authors, this will just add complexity, and not reduce any. I find the nullable pattern way better as it is more explicit than hiding it into a construct from functional program that really shouldn't be applied in OOP. Did you try to bench this in terms of added heap allocations?
@gileee
@gileee Жыл бұрын
You wouldn't return the Book or Author objects in your response anyway. You'd have a Response contract that obviously would have all the fields reduced anyway. Functional programming has never been known for it's performance, only elegance.
@geesysbradbury3211
@geesysbradbury3211 Жыл бұрын
nowadays, more money is lost because of unmanageable code than on performance
@forthegod
@forthegod 4 ай бұрын
Schrödinger approves that video =)
@zoran-horvat
@zoran-horvat 4 ай бұрын
@@forthegod One cannot tell during the video :)
@albertoviceconti8579
@albertoviceconti8579 10 ай бұрын
Great class and great idea. Thank you
@KA-wf6rg
@KA-wf6rg Жыл бұрын
I remember learning the optional object pattern from your videos on Pluralsight years ago. I really enjoyed using it but, perhaps because it requires a custom implementation with each new project/company I go to, I have fallen out of practice using it.
@zoran-horvat
@zoran-horvat Жыл бұрын
True. I would rather see a proper implementation in dotnet that includes automatic conversions to and from the underlying type and pattern matching, all supported natively.
@livingdeathD
@livingdeathD Жыл бұрын
Excellent explanation, I only have one question, regarding performance and memory consumption, since I see that objects are constantly being created every time the map is called, if you can clarify a little I would appreciate it. ❤‍🔥
@zoran-horvat
@zoran-horvat Жыл бұрын
The option type can be implemented as a struct that wraps a nullable reference. In that case, there would be no allocation of new objects. Each assignment would be equal in performance to assigning a common reference. And yet, you would have all the benefits of a monadic type.
@tarekalkhalili3180
@tarekalkhalili3180 Жыл бұрын
Thanks for the video How can we map this type to the database using ef core for example?
@zoran-horvat
@zoran-horvat Жыл бұрын
That is a bit longer story, which I plan to cover in one of the subsequent videos.
@vitaliliubarski9321
@vitaliliubarski9321 3 ай бұрын
Hi Zoran, thank you for a brilliant null reference solution. I wrote my GetBookLabel (before I saw your version of it while watching this video) and it's GetBookLabel(Book book) => book.Author .Map(x => $"{book.Title} by {GetLabel(x)}") .Reduce(book.Title); Do you think it's a good alternative to yours? If not - why? Thank you anyway.
@vincentvogelaar6015
@vincentvogelaar6015 2 ай бұрын
Compile time errors > run time errors. Isn’t that what this all boils down to? If we agree on this, isn’t the monad deterministically superior? It can’t come down to preference.
@billy65bob
@billy65bob Жыл бұрын
14:20 that's actually the thing I hate about Java, or rather how Java developers approach things. They'd have a Book class, and then a derived BookWithAuthor. Likewise they'd split Author into both Author and a derived AuthorWithLastname. Extrapolate this to a large code base, and you've got an insurmountable hierarchy I don't want to deal with. On a side note, couldn't you make Map and Reduce generic extensions, similar to LINQ, so you don't need the wrapper? i.e. something like: public static TResult? Map(this T? value, Func map) => value != null ? map(value) : null; public static T Reduce(this T? value, T defaultValue) => value ?? default
@zoran-horvat
@zoran-horvat Жыл бұрын
Traditional Java was exhibiting much of the issues connected with pure OOP - most notably bloated code and too many classes. It has changed iver the years to adopt more streamlined designs, but I am not sure if programmers are lagging behind these changes. I still see a lot of dirty Java code. Regarding nullable extensions, that is a viable option. The core idea is to wrap null tests into a monad and this remove them from code that operates on the object. Extension methods can implement that monad.
@avishaybenshimol3064
@avishaybenshimol3064 Жыл бұрын
As always, very insightful! Are there any pitfalls when used with entity framework core 7?
@zoran-horvat
@zoran-horvat Жыл бұрын
I plan to make another video to show optional objects in an ASP.NET application with EF Core.
@DmitriNesteruk
@DmitriNesteruk 6 ай бұрын
The use of terminology here is not 100% correct, because normally `Reduce()` does a completely different thing. The correct term here would be something like `Otherwise()` or something to that effect. `Map()` is more correct though you could equally call it `Select()` (per LINQ terminology), `Take()` or something else. Also, in `GetLabel()`, the initial call to `person.` can throw if it's null, and this call isn't handled here by the Maybe monad.
@hakanakdag9491
@hakanakdag9491 4 ай бұрын
I see your point but he assumes that in both getlabel methods book and person objects cannot be nulls. Method doesn’t accept null values. If method parameter would be Person? Or Book? Then it would be possible to pass null values.
@heischono4917
@heischono4917 5 ай бұрын
What about the performance, handling Option vs Nullable types?
@zoran-horvat
@zoran-horvat 5 ай бұрын
Avoid collections of optionals and you'll be fine.
@MehediHasan-xd6rj
@MehediHasan-xd6rj 6 ай бұрын
Please take 1000 likes and thanks from me for your excellent Job. I am following you from the Pluralsight course. I have become a fan of your KZbin videos.
@ivandrofly
@ivandrofly Жыл бұрын
9:20 - Monad 13:20 - Functional c# course in Pluralsight
@kimfom
@kimfom 2 ай бұрын
NDD: Null Driven Development 🔥🔥🔥
@kelton5020
@kelton5020 6 ай бұрын
I like that the caller doesn't have to check for nulls, but I feel like the tradeoff is it's not as intuitive or readable as null checks/coalescing.
@zoran-horvat
@zoran-horvat 6 ай бұрын
It starts being intuitive when you accept function application as the principle. That is a giant leap towards monadic flows.
@rpchost
@rpchost Жыл бұрын
Thanks Mr. Zoran for this unique interesting video your channel will hit 100 000 subscriber in no time
@zimpoooooo
@zimpoooooo 8 ай бұрын
I thought Reduce generally meant combining the output from Map, but here it is used as an Else. Isn’t it?
@ks1970in
@ks1970in Жыл бұрын
brilliant. haven't tried - but hopefully should be easily mapped to DB field in EF core configurations. Also - what is the performance impact? can these be "inlined"?
@zoran-horvat
@zoran-horvat Жыл бұрын
I am sure that compiler will consider inlining any of the operators. On top of that, you can use MethodImpl(MethodImplOptions.AggressiveInlining) attribute on methods to inficate the compiler that it should avoid making superfluous calls to the monad itself.
@BelaSzilagyi
@BelaSzilagyi Жыл бұрын
Excellent video, thank you!
@SerhiiZhydel
@SerhiiZhydel Жыл бұрын
I watched your video very carefully and read your comments, and this Do/Map/Reduce pattern looks very interesting to me, so thanks for sharing this wonderful idea! But could you give a more full answer on if it is worth using it on the client side on frameworks like xamarin, wpf, or especially unity that eventually serializes almost all the types you create? And how do you handle serialization in this case? In json it will result in having an extra level nested object where user expects to see a field, right?
@zoran-horvat
@zoran-horvat Жыл бұрын
That is a very good question. When it comes to transfer, serialization, persistence and other uses external to the model, I still use native types. When Option is implemented as a struct, then there is no overhead in converting it to and from a nullable variant of the contained type, for example. I am just doing that conversion on the boundary of the model so that only the model uses, and benefits from the use of optional objects.
@SerhiiZhydel
@SerhiiZhydel Жыл бұрын
@@zoran-horvat ok I see, then it's probaly more a web-developer tool. thanks for the clarification!
@kimfom
@kimfom 2 ай бұрын
This is so beautiful
@soverain
@soverain Жыл бұрын
I like this approach very much. I have a question: how would you go about using this option type with a function that does not return a value? Like updating a database entry. Would you always end the sequence by calling Reduce()? What if you can’t provide a default value but just need to exit the function?
@zoran-horvat
@zoran-horvat Жыл бұрын
You can define another method (I usually call it Do), which optionally invokes an action.
@soverain
@soverain Жыл бұрын
@@zoran-horvat I guess this Do method should return the option as it is, so we can continue chaining?
@zoran-horvat
@zoran-horvat Жыл бұрын
@@soverain No, I mean the method would be defined on the Option itself, just like the Map method is.
@SirJohnK
@SirJohnK Жыл бұрын
Really, really interesting! Enlightning as a none functional C# developer! 👍
@legittaco4440
@legittaco4440 4 ай бұрын
I don’t like that the map function checks for null, what if the function wants to map null to a non null value?
@zoran-horvat
@zoran-horvat 4 ай бұрын
@@legittaco4440 What is the use case where you have bot null and None as meaningful and mutually dufferent values?
@cd1131
@cd1131 2 ай бұрын
This is so great - but EF does not support conversions between nullable columns and not nullable types like Option. 😢
@zoran-horvat
@zoran-horvat 2 ай бұрын
@@cd1131 It does, via conversion types. There is a property to override which tells whether to handle nulls or not - the default is "not".
@ShiyalaKohny
@ShiyalaKohny Жыл бұрын
Love your videos. Quick question, why is option not a struct?
@zoran-horvat
@zoran-horvat Жыл бұрын
It can be. Actually, in the GitHub reoo, it is the struct. On the plus side, it takes less memory and CPU. On the negative side, it doesn't support pattern matching.
@muhamedkarajic
@muhamedkarajic Жыл бұрын
Great introduction!
@pmcgee003
@pmcgee003 9 ай бұрын
I very much like your two videos on Option that I have seen .. but I have an issue with your naming of Reduce for OrDefault. Reduce is well established (as in Map-Reduce) for repeatedly applying a binary operator T->T->T I'd also, personally, rather see bind named as such, given it's really Map followed by Join.
@zoran-horvat
@zoran-horvat 9 ай бұрын
This naming follows from the observation that an optional object is nothing but a sequence with no more than one item in it, a sequence with a constraint. Therefore, the Map-Reduce principle applies to it natively.
@pmcgee003
@pmcgee003 9 ай бұрын
@zoran-horvat I have to disagree. It's really a mapping of a function that returns Just(x) or Default. Reduce wouldn't change type. It would be T+T -> T
@Alguem387
@Alguem387 Жыл бұрын
Is there any valid reason to not just use Option as a readonly struct? Since it dosent make sense to mutate it nad all it does is wrap a value when it wraps a reference it just holds it it being a class adds an extra level indirection, that dosent seem to be of any use. Im i missing something?
@zoran-horvat
@zoran-horvat Жыл бұрын
In the GitHub repository I have implemented it as a record struct wrapping a nullable reference. That would improve performance, but at the expense of two issues. One, you cannot support optional value types and optional reference types with one type - the implementation leaks into the public API. The other issue is that, not being polymorphic, the struct Option cannot be a subject to pattern matching.
@anarhistul7257
@anarhistul7257 Жыл бұрын
When would you consider this pattern overkill? I could see it used almost everywhere while not always worth the cost. I could just incapsulate GetLabel as a function on the object without the use of Option with some basic null checks. While not stopping me from accessing null properties as long as teammates aren't idiots this works just fine. (as long as they're not idiots)
@zoran-horvat
@zoran-horvat Жыл бұрын
Any proper domain model should avoid depending on null in my opinion, and that is not the question of someone not being an idiot. Simply put, null carries no information. It is not telling anything in terms of the domain. And, on top of that, it is prone to design and runtime errors when programmers propagate nullable references through the entire domain only to keep the compiler quiet. If you were a Rust programmer, you wouldn't even ask that question. There, the entire language is driven by Option and Result types that are equivalent to Maybe and Either types in Haskell, for instance - and nobody ever complained about that! Working with optional objects in domain modeling is the state of the mind. Once you switch to that mode, you will never want to use a null in modeling again. It's not just me. I have testimonials from several colleagues, members of teams I led in the past, who were reluctant to accept that at first, but a year later they come to me saying: Never nullable reference again. Nullable references remain in non-OO parts of the model: Persistence model, UI (if no other solution), serialization, etc.
@vionoche
@vionoche Жыл бұрын
A great video, thank you! How do you think about using a struct instead of a class for the Option type? I suppose, the struct may consume less memory than the class. Anyway, I like this approach with Options, amazing code =)
@ar_xiv
@ar_xiv Жыл бұрын
You can't initialize _option as null with a struct
@vionoche
@vionoche Жыл бұрын
But in this Option we use nullable types. When we write `private T? _object = null;` it is the same sort as `private Nullable _object = null;`. Nullable types are structs themselves so you can use a struct of structs. I've just written an example with struct Options and it works. However, I've found an answer why using the struct Option is a bad idea: The memory layout of a nullable structure is just like that of the non-nullable version plus a boolean indicating whether there is a valid value. Compared to a class, a structure that contains nullable types will have an additional boolean in its memory layout for each of its nullable fields. That is why the struct Option will consume a little more memory than a class.
@sinan720
@sinan720 Жыл бұрын
Yea, thats why Nullable is a struct. Writing extension methods for Nullable is better than creating Option type in my opinion
@davidtaylor3771
@davidtaylor3771 Жыл бұрын
We are never going to get more than a small number of developers using a technique like this unless it is more elegant and built into the language.
@Sanabalis
@Sanabalis Жыл бұрын
You made these Optional Objects really interesting and I'd love to use them. However, I am unsure on how to implement a specific use case. What if I have two (or more) optional values in the class, and I have to do 4 different things, depending if both values are present, just the first, just the second or neither. What would be the proper way of mapping/implementing that use case? Basically, how to implement the following (expanded for clarity): if (obj.a is null && obj.b is null) return n(); if (obj.a is not null && obj.b is null) return a(); if (obj.a is null && obj.b is not null) return b(); return ab();
@zoran-horvat
@zoran-horvat Жыл бұрын
That is the hard part about possibly missing objects. That same problem exists with any other representation, like nullable objects. What I normally do when I face a pair of optional objects is to define an operation where only one (e.g. the second one) is optional, as an intermediate step. Then resolve the first object and, if exists, call this intermediate operation. That operation, in turn analyzes the second object and applies the definite two-argument function to both objects on success. Alternative is to transform the pair of optional objects into an optional pair, using another helper operator.
@zoran-horvat
@zoran-horvat Жыл бұрын
P.S. There is another aspect of the question you asked: How did you get into the position of having two optional objects? Why didn't the operation resolve the first object before stepping to fetch the second one, so to terminate early if the first object is missing?
@Sanabalis
@Sanabalis Жыл бұрын
@@zoran-horvat I just added the following into the Option class (and similar to ValueOption): public Option OrElse(Func orElse) => _value is not null ? this : orElse(); This made it easy to handle missing objects. Example code: ItemOne.Map(WithFirst).OrElse(WithoutFirst);
@zoran-horvat
@zoran-horvat Жыл бұрын
@@Sanabalis That is a useful variant.
@tomaszpajak9227
@tomaszpajak9227 5 күн бұрын
Why use static Create method instead of constructor directly ? It's not the first time I see this pattern.
@zoran-horvat
@zoran-horvat 5 күн бұрын
@@tomaszpajak9227 That is a coding practice common in FP. The Create method had a couple of advantages over the constructor. It is a method, which a constructor is not. That means you can pass the Create method around and assign it to delegate types. It also has a return type, which a constructor doesn't have, so it can return more convenient functional types, such as Option or Result.
@gr-gx4zy
@gr-gx4zy Жыл бұрын
Does it make sense on front-end side to do something like this with data that comes from BE? I would like to get rid of null and undefined in typescript but not sure if that makes sense. I know JQuery did that long time ago.
@smwnl9072
@smwnl9072 Жыл бұрын
Try Elm. It’s a FE framework based on FP.
@1Eagler
@1Eagler 10 ай бұрын
3:50 why not return empty? How do these classes are saved on database?
@zoran-horvat
@zoran-horvat 10 ай бұрын
Empty string is not the same as no string - it would indicate that the string exists, and its value is empty. Optional objects can be saved to nullable fields in the database record, because that is the way relational databases are handling nonexistent values.
@larryd9577
@larryd9577 Жыл бұрын
The Java guidelines recommend holding null in fields instead of options. You shouldn't care what the state is, that is why it is private. :) And return from its getter an empty optional. Calculating sounds so expensive, when in essence it returns a static EMPTY object, or wraps the result in a low footprint wrapper and all by calling just one function `Option.ofNullable(_state)`.
@gileee
@gileee Жыл бұрын
People in java mostly use Lombok anyway to implement getters and setters and Lombok doesn't support getters that return Optional. Seems to be because the devs think it's bad code. It seems they consider Optional itself to be bad code for some reason. So unless you want to write all those getters and setters in full by hand every time, good luck. Considering it's mostly accepted in the Java world that you shouldn't have Optional in fields, it seems the "easiest" way would be to call Optional.ofNullable by hand whenever you access a nullable field.
@gileee
@gileee Жыл бұрын
@@dispatch-indirect9206 Handling "presence or absence" of fields with derived classes only gets you a million classes in your code. You should rarely have more than 1 level of inheritance.
@ar_xiv
@ar_xiv Жыл бұрын
Is there a way to use this with just a normal constructor instead of lambdas and this create function?
@zoran-horvat
@zoran-horvat Жыл бұрын
You can expose two constructors, or just one with a nullable argument, but that might be confusing to callers. Mapping and reducing, on the other hand, are meant to bind a function to the optional content. What else can we do but receive a Func delegate?
@manmohanmundhraa3087
@manmohanmundhraa3087 Жыл бұрын
Can we specify multiple property which may contain null value before using Map ?
@zoran-horvat
@zoran-horvat Жыл бұрын
Things become progressively more complex when multiple optional objects are used in the same expression. To see it better, try to imagine the same expression with nullable references - that would grow more complex as well, but multiple branching instructions would be easier to imagine. One way to address the problem is to cut the problem into smaller pieces, implemented by one method each. A method would then only manage one optional object and produce a result, either another optional object, or a proper (non-optional) object. Then combine those smaller methods into a large expression. I use this design style of separating optional expressions into sub-expressions a lot.
@kleinpoe
@kleinpoe Жыл бұрын
Cant you write extensions methods to T? that do Bind and reduce for you?
@zoran-horvat
@zoran-horvat Жыл бұрын
That is possible, but you would have to implement it separately for nullable value types, and the dolution is also limited to one object. For instance, that wouldn't work when implementing the Either type, which is, in a way, an extension to the idea of optional objects. It is therefore common to implement monads as full-blown types.
@kleinpoe
@kleinpoe Жыл бұрын
@@zoran-horvat Yes that is unfortunate. I came across your pluralsight courses and you motivated me to explore the functional world and it is great. Thank you for that. Unfortunately my coworkers are not very incentivized to introduce the functional monads to our code, they rather want to use the nullable reference types... so I am looking for ways to still get at least some functional tools. But it seems that I will not get the either monad then... :-(
@1Eagler
@1Eagler 10 ай бұрын
Actually, it was called Aristotle of Nikodimos - his father
@zoran-horvat
@zoran-horvat 10 ай бұрын
You have a Greek name, is that how you know it? It is new information for me, I'll remember it. Thanks!
@1Eagler
@1Eagler 10 ай бұрын
@zoran-horvat my name helped. And my age - started with Pascal.
@f13775
@f13775 Жыл бұрын
Zoran, greetings from Macedonia! Why just not to introduce FullName method and incapsulate the logic of getting the full name into the Author object? it looks way more complex, to use map+reduce just to get rid of the null, I understand that this is just simple example, would be great if you can show real production code where this complexity is pay off
@zoran-horvat
@zoran-horvat Жыл бұрын
Mainly because there are multiple pieces of logic that pertain to the same piece of data. You would normally return an optional object from a method or a property getter and then leave mapping to the implementer of whatever logic is needed.
@f13775
@f13775 Жыл бұрын
@@zoran-horvat Thank you for response Zoran, and from your experience where in application you would typically implement such approach ( maybe from your experience working on previous real production projects where you implemented such logic ?) Thank you!
@zoran-horvat
@zoran-horvat Жыл бұрын
@@f13775 Actually, that is a very frequent case in any domain. Think of any situation where a method would return a nullable type, and that is a viable case for optional objects.
@f13775
@f13775 Жыл бұрын
@@zoran-horvat will try to implement this as part of refactoring in our pretty legacy project, hope the Optional object will pass code review from my team peers:), thank you for you feedback!
@10199able
@10199able Жыл бұрын
Warning: M-word at 7.52
@biskitpagla
@biskitpagla Жыл бұрын
That tweet about not wanting to have two ways to do something is kinda hilarious because there're like - not two - but three or more ways to do absolutely anything and everything in C#. C# even pioneered some of the weaponry that'd later be used to divide entire libraries into multiple worlds like async-await, to name one. That Java convention is controversial but has some truth/insight to offer. Java doesn't have non-nullable reference types. The compiler literally cannot help you in most cases and your best bet is to sprinkle your code with third-party annotations, and pray and hope that your IDE picks things up from there on. So, there's an equally funny and depressing chance that a variable of Optional itself is null. As a result, some Java experts just decided to ignore this botched implementation of what is otherwise an excellent pattern and told people to pretend it doesn't exist. Others, however, salvaged what is there and chose to keep using for documentation and quality-of-life purposes.
@vlastimiladamovsky7867
@vlastimiladamovsky7867 Ай бұрын
👍👍👍👍👍
@ivandrofly
@ivandrofly Жыл бұрын
Keyword: "Reduce" example
@Astral100
@Astral100 Жыл бұрын
Unfortunately this is very complex for most developers. In my experience many developers struggle even with basic concepts. If I were to write a code like this it will become much harder to read and understand for other/future developers. (Who are usually not very bright at the best of times) I believe writing a simple (to read and understand) code is much more important that optimising the code to this degree.
@gileee
@gileee Жыл бұрын
Those junior devs will write horrible code anyway and need a lead dev to manually review their pull request until they learn no matter what kind of style or architecture you use. There's no getting around that. They need a mentor. The Maybe object just hides and even removes some superfluous null checks that permeate all codebases. It also removes the need to think about naming some variables inside functions since you just chain Map calls.
@marcotroster8247
@marcotroster8247 3 ай бұрын
This isn't a monad. You should watch Mark Seeman's talk on how to pull out the value out of a monad. Because you don't. You inject the desired behavior with lambdas.
@guai9632
@guai9632 Жыл бұрын
why stop here? use nullable optional of an optional everywhere, I bet it will reduce your code even further :)
@zoran-horvat
@zoran-horvat Жыл бұрын
Or just continue using monads the way everybody else does for decades...
@alexander_nunezf
@alexander_nunezf Жыл бұрын
It remind me what they do in Rust.Also, they have something alike regarding exception handling there too.
@zoran-horvat
@zoran-horvat Жыл бұрын
Yes, in Rust everything is Option and Result. Exceptions (called panic in Rust) are what assertions are in dotnet and they terminate the process.
@todortodorov2632
@todortodorov2632 Жыл бұрын
But finally the Reduce method can return null and the client code will be forced to check if the result is null before using it. I do not see many sense using this implementation of Option. I prefer using the way when the option wraps the object in a collection with one element and then execute an action looping through the collection elements. I think I saw it in one of your Pluralsight videos many years ago.
@zoran-horvat
@zoran-horvat Жыл бұрын
Why would you return null?
@ai3388
@ai3388 Жыл бұрын
0. Your function GetLabel contains an error, last name can be empty. simple and easy to read function can be: string GetLabel(Person p) => $"{p?.first} {p?.last}".Trim(); 1. too much boilerplate code with optional 2. functional way for the simplest code looks unnatural in c# and it will be more ugly with more complex program. code is written once and read many times. 3. memory overhead. let's imagine we have 1M records. 4. we significally complicate the code for the sake of compiler warnings. p.s. and i dont think that nullable reference type is a good idea. Especially when we have null forgiving operator. #nullable enable object? nullable = null; object nonNullable = nullable!; var deref = nonNullable.ToString();
@zoran-horvat
@zoran-horvat Жыл бұрын
Optional objects are useful in a functional design where everything that is important already has a function that holds it. In that case, an operation on an optional object simply applies a preexisting function, making code shorter and more readable than the variant based on branching. Memory overhead problem is completely removed with a struct option.
@ai3388
@ai3388 Жыл бұрын
​​@zoran-horvat yes, they are useful. i thought a little about these functions(map, reduce in this case) as the feature of c# for nullable types, it makes sense. But without additional class.
@thygrrr
@thygrrr Жыл бұрын
Hmm, 15:40 still makes no sense to me.
@alfflasymphonyx
@alfflasymphonyx Жыл бұрын
I kind of agree with you. I understand the principle and the use of Option. However, I believe the issue might be the naming of functions which then used in the code renders the readibility more difficult.
@grantofat6438
@grantofat6438 Жыл бұрын
Answer: Learn to program properly.
@zoran-horvat
@zoran-horvat Жыл бұрын
Speaking of programming properly, there was a guy who explained (using quite strong language) that it is "programmer's duty to properly deallocate memory in C++". The result of accidentally not doing so, in that particular case, was 70 people dead by the end of the day. I would argue that you never programmed a nuclear plant or anything of that sort before throwing the term "program properly" into the discussion.
@GeoffInfield
@GeoffInfield 5 ай бұрын
Java has Optionals and moving to C# is horrifying. The inconsistent behaviour and blurring of lines is apalling. In Java you're treated like an adult so everything is precise - int is primitive/value and Integer is a class/reference, ditto boolean/Boolean - but in c# we have int which is apparently a primitive/value that defaults to zero and has a constructor like an object/reference so which IS it? String - sorry, string - also has a constructor but it DOESN'T default to "" so wtf? Java String is only an object/reference and it behaves like every other object/reference, no guessing. I'm hating C# so much 😢
@zoran-horvat
@zoran-horvat 5 ай бұрын
Are you sure "hate" is the right choice of a word. If you invested a bit into understanding these differences, you would surely find that Java doesn't have the definition of types passed by value and has no definition of generic types. What you call "precision" in Java is the only way there is. And so you will find all sorts of issues due to those shortcomings that were never solved (but should have been). Try to allocate a generic array and then specialize it to ints. Is that 10 lines of code in Java and execution time going 40x up? That is not "precision".
@larryd9577
@larryd9577 Жыл бұрын
Why don't you build it yourself, we did it 3 years ago.
@zoran-horvat
@zoran-horvat Жыл бұрын
To build what?
@larryd9577
@larryd9577 Жыл бұрын
I mean for the fact, that the C# devs are not ready to support options, but you did it in the end. Sorry should have watched it till the end.
@ParkourGrip
@ParkourGrip Жыл бұрын
I am sure the map reduce pattern can be used with nullable values too. In Kotlin i would do nullableVariable?.let { it.someProperty } ?: defaultValue . I'm not super familiar with C# but im sure that you can implement your own let function from Kotlin if it does not already exist in the standard liberary. The only real advantage i see with Optional values is for things like storing optional values in a map. For example when we have a Map the return value of the map.get(key) method is going to be String?. But we have no idea if the returned value is null because null is stored in a map or because the key is not found inside the map. When using optionals, the map would be of type Map and the return type would be of the map.get would be Option. Now everything is clear from the return value.
@ParkourGrip
@ParkourGrip Жыл бұрын
"let" is just a function that takes a lambda expression as a argument and returns the value that the lambda expression returned. "it" is a implied default name of the single argument lambda in kotlin. You could write nullableVariable?.let { it -> it.someProperty } ?: defaultValue . It's the same thing.
@zoran-horvat
@zoran-horvat Жыл бұрын
You can implement such a function in C# as well, as an extension to a nullable reference. One problem with that is that it cannot support value types consistently.
@alxjones
@alxjones Жыл бұрын
I really hate the idea of storing optional/nullable values in a map. A fundamental feature of a map is that a key exists if and only if a value exists on that key. I would re-evaluate the decisions that lead to the choice to break that feature before trying to use it to justify a different approach to null safety. In the event that you do need to distinguish between multiple fail-states, neither nullable nor optional is going to do what you want. Instead, you should be using a Result monad, e.g. from `kotlin-result`.
@zoran-horvat
@zoran-horvat Жыл бұрын
@@alxjones I agree with you. In my opinion, not having an object mapping to a key is equivalent to mapping that key to null or some other indication that the object is missing, hence storing optional objects into a map is an oxymoron. On top of that, C# dictionary throws if the key is null, which I also agree with.
@ParkourGrip
@ParkourGrip 3 ай бұрын
​@@alxjones One immediate example that comes to mind is when a Map is used for cashing results of expensive operations that produce optional results. If a key does not exist in a map, that means that the expensive operation has not been cashed for this key. If a key exist and the value is null/None, that would mean that the calculation has been cashed, no need to do it again, the result of the operation is null/None. If the key exists and value is not-null/Some, that means that the calculation is cashed and result is not-null/Some. Still the point stands. The person that implemented the Map in a language that uses optionals instead of nulls never had to think about supporting this usecase. Optional is just like any other type T. While the person implementing the Map type in a null based language had explicitly decide not to support this usecase and throw exceptions if someone tries to store null values, or disallow nullable types in the constraints of the generic value type.
Using GitHub Copilot to Write Complex Code | Step-by-step Tutorial
16:01
Why Favor Object Composition Over Class Inheritance? A Deep Dive
19:00
The Singing Challenge #joker #Harriet Quinn
00:35
佐助与鸣人
Рет қаралды 45 МЛН
Turn Off the Vacum And Sit Back and Laugh 🤣
00:34
SKITSFUL
Рет қаралды 4 МЛН
Manage Nulls Like a Boss and Never Fail!
21:43
Zoran Horvat
Рет қаралды 14 М.
Don't throw exceptions in C#. Do this instead
18:13
Nick Chapsas
Рет қаралды 262 М.
Object-Oriented Programming is Embarrassing: 4 Short Examples
28:03
Brian Will
Рет қаралды 2,1 МЛН
17 Pieces of C# Syntax That Make Your Code Short
12:41
Zoran Horvat
Рет қаралды 26 М.
8 await async mistakes that you SHOULD avoid in .NET
21:13
Nick Chapsas
Рет қаралды 315 М.
Build Your Own Option Type in C# and Use It Like a Pro
18:31
Zoran Horvat
Рет қаралды 15 М.
Remove Messy Constructor Calls | Clean Code
12:19
Zoran Horvat
Рет қаралды 15 М.
The New Option and Result Types of C#
15:05
Nick Chapsas
Рет қаралды 79 М.