The New Option and Result Types of C#

Nick Chapsas
7 Aug 202415:05

Summary

TLDRIn this video, Nick discusses the highly anticipated feature of C#, type unions, focusing on the introduction of the 'Result' and 'Option' types to the runtime. He explains their purpose, how they work in other languages, and their potential impact on C# development. Nick demonstrates their use through examples, comparing them to nullable types and exception handling. He emphasizes the benefits of these types for safer and more robust code, and encourages viewers to share their thoughts on this new feature.

Takeaways

  • πŸš€ Nick introduces the concept of discriminated unions, also known as type unions, a highly requested feature in C#.
  • πŸ” Two native types, 'Result' and 'Option', are being introduced to the runtime, which are causing some confusion among developers.
  • πŸ’‘ Nick explains that these types can be used to create safer and more robust code by explicitly handling different outcomes.
  • πŸ“š He provides a demonstration using a simple console application to show how nullable types can be problematic and how 'Option' and 'Result' types can address these issues.
  • πŸ›‘ The script mentions that using exceptions for flow control is considered bad practice and can be slow, which is where 'Result' types can improve performance.
  • πŸ“ Nick shows how to refactor existing code to use 'Option' and 'Result' types, emphasizing the need to handle all possible outcomes.
  • πŸ”„ The video script also touches on the use of language extensions to add functional elements like 'Option' and 'Result' to the code base.
  • πŸŽ“ Nick discusses the learning curve associated with adopting these new types and the benefits they bring to codebases in terms of safety and clarity.
  • πŸ“ˆ He highlights the importance of having a standardized approach to error handling and optional values across different libraries for better interoperability.
  • πŸ—£οΈ The script encourages viewers to share their thoughts on the new features and reminds them that Microsoft is considering community feedback for future updates.
  • 🎁 Nick promotes two new courses on DOM, Git, and GitHub Actions, authored by Scott Hanselman, offering advanced insights into these topics.

Q & A

  • What is the most requested feature of C# that Nick mentioned in the video?

    -The most requested feature of C# mentioned by Nick is discriminated unions, which are more appropriately named type unions.

  • What are the two native types introduced in the runtime according to the proposal Nick discussed?

    -The two native types introduced in the runtime are the result type and the option type.

  • What is the main purpose of the 'result' type in programming?

    -The 'result' type is used to represent a value that can be either a success or an error, providing a way to handle different outcomes in a method call without using exceptions for flow control.

  • What does the 'option' type represent in programming?

    -The 'option' type represents a value that can either be 'some' with a specific type of value, or 'none', indicating the absence of a value. It is used to avoid nullable types and enforce handling of both present and absent cases.

  • How does Nick demonstrate the use of 'option' and 'result' types in his sample console application?

    -Nick demonstrates the use of 'option' and 'result' types by showing a 'getUser' method that returns an 'option' of 'user' and a 'createUser' method that returns a 'result' of 'user', handling both success and failure scenarios.

  • What is the issue with using exceptions for flow control in applications according to Nick?

    -Using exceptions for flow control is considered a bad practice because exceptions are slow and can lead to less maintainable code. It is also a common criticism that it is not an efficient way to handle expected outcomes.

  • What is the advantage of using 'result' and 'option' types over nullable types?

    -The advantage of using 'result' and 'option' types over nullable types is that they enforce handling of all possible outcomes (success, failure, or absence of value), leading to safer and more robust code.

  • Why does Nick believe that adding 'option' and 'result' types to the BCL (Base Class Library) is a net positive?

    -Nick believes it's a net positive because it provides a common type that everyone will use, which can lead to a consolidation of libraries and NuGet packages, making the codebase more standardized and interoperable.

  • What is the current status of the 'option' and 'result' types in C# according to the video?

    -As of the video, 'option' and 'result' types are available through NuGet packages and language extensions, but they are not yet part of the standard C# language or runtime.

  • What is the significance of the 'match' expression in the context of 'option' and 'result' types?

    -The 'match' expression is used to handle the different cases of 'option' and 'result' types exhaustively. It allows for pattern matching to either handle the 'some' or 'none' case for 'option' types, and the 'success' or 'failure' case for 'result' types.

  • How does Nick suggest handling the potential null reference exception when using nullable types?

    -Nick suggests handling the potential null reference exception by adding an if check to ensure the object is not null before accessing its properties, or by using 'option' and 'result' types which enforce handling of all cases.

Outlines

00:00

πŸš€ Introduction to Discriminated Unions and New Runtime Types

Nick introduces the concept of discriminated unions, also known as type unions, which are a highly requested feature in C#. He discusses the addition of two new native types, 'Result' and 'Option', to the runtime, which aim to address common programming challenges. Nick explains that these types are designed to improve code robustness and library consolidation. He also mentions the use of nullable types and the potential issues with null reference exceptions, setting the stage for a deeper exploration of these new types in the context of C# development.

05:02

πŸ›  Exploring Option and Result Types with Language Extensions

This section delves into the practical application of 'Option' and 'Result' types using language extensions. Nick demonstrates how to refactor existing code to leverage these types for safer and more explicit error handling. He contrasts this approach with traditional exception handling and nullable types, highlighting the benefits of having to explicitly handle all possible outcomes. The summary also touches on the performance implications of exceptions and the advantages of using 'Option' and 'Result' for a more functional style of programming.

10:06

πŸ” Discussion on the Necessity and Future of Option and Result Types

Nick wraps up the video by discussing the rationale behind the introduction of 'Option' and 'Result' types in C#, despite the availability of similar constructs in .NET and language extensions. He emphasizes the importance of standardization and interoperability between libraries. The summary also includes a call to action for viewers to share their thoughts on the new feature and an invitation for feedback, noting that Microsoft is actively considering community input for the development of these types in C#.

Mindmap

Keywords

πŸ’‘Discriminated Unions

Discriminated unions are a type of data structure in programming that can hold one of several different types of values, each distinguished by a label. In the context of the video, discriminated unions are discussed as 'type unions', a feature in C# that allows for more expressive and safe code by clearly defining the possible states of a variable. The video mentions that this feature is highly requested and will be introduced in the future version of C#, enhancing the language's capability to handle different types of data.

πŸ’‘Type Unions

Type unions, as mentioned in the script, are a concept in programming that allows a variable to be one of several types. The video explains that type unions are more appropriately named and are a significant feature in the evolution of C#. They are a way to express that a function or a variable can hold one of multiple types, which is a powerful concept for writing more expressive and safer code.

πŸ’‘Result Type

The result type is a concept introduced in the video as a native type in the runtime, which is used to represent the outcome of an operation that can either be a success or a failure. It is related to the main theme of the video as it is part of the new features in C# that aim to provide safer and more expressive code. The video script uses the result type to demonstrate how operations can be handled more elegantly without the need for exception handling.

πŸ’‘Option Type

The option type is another native type discussed in the video, which represents a value that can either be 'some' value of a specific type or 'none'. It is used to handle the possibility of absence of a value more safely than nullable types. In the script, the option type is shown as a way to improve code safety by forcing developers to handle both the presence and absence of a value explicitly.

πŸ’‘Nullable Types

Nullable types in C# are types that can hold an additional 'null' value, indicating the absence of a value. The video script discusses nullable types in the context of their limitations and how they can lead to potential null reference exceptions. The introduction of option and result types is presented as a solution to these issues, promoting safer coding practices.

πŸ’‘Validation Exceptions

Validation exceptions are a type of exception used in programming to indicate that input or data being processed does not meet certain criteria. In the video, validation exceptions are thrown when an email is invalid or already exists in a database. The script uses validation exceptions to illustrate common coding patterns and the problems associated with exception handling for flow control.

πŸ’‘Null Reference Exception

A null reference exception occurs in programming when an attempt is made to access a member on a null object reference. In the video, the script mentions the potential for a null reference exception when dealing with nullable types, such as when trying to access a property of a potentially null user object. This highlights the need for safer ways to handle the absence of a value.

πŸ’‘Language Extensions

Language extensions, as mentioned in the script, refer to additional packages or libraries that extend the functionality of a programming language. In the context of the video, language extensions are used to demonstrate the concept of option and result types before they are natively supported in C#. They provide a way to implement functional programming elements and safer coding patterns.

πŸ’‘Exception Handling

Exception handling is a programming technique used to manage errors and other exceptional conditions that occur during program execution. The video script criticizes the use of exceptions for controlling application flow, as it can lead to performance issues and is considered bad practice. The introduction of result and option types is presented as a way to avoid unnecessary exception handling.

πŸ’‘Interoperability

Interoperability refers to the ability of different systems or components to work together. In the video, the addition of standard option and result types to C# is discussed as a way to improve interoperability between libraries, ensuring that all developers have a common understanding and usage of these types, leading to more consistent and maintainable code.

Highlights

Introduction of discriminated unions, also known as type unions, a highly requested feature in C#.

Discussion of two new runtime types: the result type and the option type.

Explanation of the confusion surrounding the new types and their purpose.

Demonstration of how to work with these types in other languages and their potential implementation in C#.

The benefits of having a common type in the runtime for everyone to use.

The impact of these types on the consolidation of libraries and NuGet packages.

Showcasing a simple console application to emulate database operations and exception handling.

The common practice of using exceptions for flow control and its drawbacks.

How to use the new types to avoid the performance issues associated with exceptions.

The use of Language Extensions package to demonstrate the concept of option and result types.

The difference between option types and nullable types in terms of safety and enforcement.

How result types can represent success or failure more explicitly than exceptions.

The potential for a more elegant consumption experience with type unions in the future.

The current workaround of using warnings as errors to enforce the handling of nullable references.

The introduction of new language features to handle the option and result types more effectively.

The comparison between using exceptions and result types for error handling and performance.

The importance of training developers to understand and use the new types effectively.

The call to action for feedback on the new feature from the community and Microsoft's consideration of it.

Transcripts

play00:00

hello everybody I'm Nick and in the

play00:01

previous video I talked about a brand

play00:02

new feature coming in cop at some point

play00:05

the future called discriminated unions

play00:07

and it's actually the most requested

play00:08

feature of cop really ever and they're

play00:11

more appropriately named type unions and

play00:14

one of those points in that proposal

play00:16

that we read is that there's two native

play00:18

types coming in the runtime and that is

play00:20

the result type and the option type and

play00:22

many people are very confused as to what

play00:25

those are what they do how are they

play00:28

going to work and why are we getting

play00:30

them in the first place so in this video

play00:31

I'm going to show you what those types

play00:33

are how they work in other languages and

play00:36

how they're likely to work in cop now

play00:38

this is something you can actually do

play00:40

already through n packages and Microsoft

play00:42

is addressing that in their Q&A which we

play00:44

will by the way take a look at after I

play00:47

show you the feature but I personally

play00:48

think the fact that they're being added

play00:50

is a net positive because if someone

play00:52

wants to use them they would use the

play00:53

runtime version part of the BCL so

play00:56

everyone will have the exact same common

play00:58

type and this will become very very

play01:00

powerful for consolidation of libraries

play01:02

and Y packages so let me show what I

play01:03

have here I have a simple console

play01:05

application and I have this user service

play01:07

over here so all I really have here is

play01:09

I'm trying to emulate something that is

play01:12

maybe calling a repository or is calling

play01:14

a DB context in anti frame or core but

play01:17

the idea is I have some sort of database

play01:18

in this case in memory and then I have a

play01:21

get user method that returns anable user

play01:24

trying to find it based on the ID and

play01:26

then I have a create user method where

play01:29

we have the email and the name very

play01:31

simple model uh we pass the email we try

play01:34

to validate it if it's invalid we throw

play01:36

an exception hey you just used an

play01:38

invalid email then I have a check on the

play01:40

database on the email because that's my

play01:41

unique identifier for this example hey

play01:43

if it already exists then we throw an

play01:45

exception and in the end if everything

play01:47

is good we create a new user very simple

play01:50

and those exceptions are validation

play01:51

exceptions so if you want to try catch

play01:54

validation exception and then get the

play01:55

message you can and you get a message

play01:57

out of it this is extremely common code

play02:00

I see this all the time in many many

play02:02

code bases now in the program.cs I'm

play02:04

going to simulate how this is supposed

play02:06

to work from a consumption standpoint so

play02:08

first you would have your user service

play02:10

and if we want to create a user would

play02:11

say user service. create user Nick chops

play02:15

and if I want ahead I tried to create

play02:16

the same user twice the first one will

play02:18

create it but as you might assume the

play02:20

second one will throw an exception hey

play02:22

this email already exists user with

play02:25

email nickd tr.com already exists makes

play02:29

sense now I could also just get the user

play02:31

and because this is a nullable type I'm

play02:34

using a user nullable over here I can

play02:37

say if a user is null then you know user

play02:43

does not exist through a message but it

play02:45

also means that if I try to do something

play02:47

like console. right line the name is and

play02:51

let me just quickly fix that the name is

play02:55

user.name well I have these yellow

play02:58

squiggly lines telling me that there's a

play03:00

possibility for a null reference

play03:02

exception because we don't really know

play03:04

if this user actually exists at this

play03:06

point because this can return null this

play03:08

is anable type so you might want to add

play03:12

an if check and we can easily do that by

play03:14

potentially checking the expression for

play03:16

null so if the user is not null do that

play03:19

and so on now something you might have

play03:21

noticed about this feature is that it

play03:23

isn't actually an error it is just a

play03:25

warning even though we know for a fact

play03:27

this can return null many languages

play03:30

actually treat this as an error cop

play03:33

because they introduced this feature in

play03:34

cop 8 they couldn't just go ahead and

play03:37

turn this into an error by default

play03:39

because it would break a lot of code

play03:40

bases if you want to adopt the feature

play03:42

so they made a warning I think that's

play03:45

bad because it sort of kills the purpose

play03:47

of the feature being there in the first

play03:48

place so what many people do is actually

play03:50

they turn this warning into an error and

play03:53

if there's a warning like this that turn

play03:55

to an eror in the code base then you can

play03:56

compile and they go ahead and they

play03:58

address it and this will actually make

play03:59

your codebase way more robust so that is

play04:01

one of the problems but the other

play04:03

problem is that we now have to throw

play04:04

these exceptions that we later have to

play04:07

catch so we have to say catch validation

play04:10

exception so validation exception over

play04:14

here then try to create the user and if

play04:17

the user hasn't been created uh we catch

play04:19

the exception we get the message and

play04:21

then maybe we return a type which means

play04:24

that if I have something like an i

play04:26

result because this is an API and I'm

play04:28

trying to return an HTTP response for

play04:31

example well what I have to say here is

play04:35

we'll take this then get HTTP response

play04:40

then I'm going to try to create this

play04:42

local function over here and I'm going

play04:44

to say try to created pass it down over

play04:47

here VAR created is this which returns

play04:51

results do created normally but I want

play04:54

to prevent the URL so I'm just going to

play04:55

say created to return and okay and then

play04:59

I'm going to say turn results. bad

play05:01

request I'm going to pass down the

play05:03

message very very quickly and then I

play05:05

have my HP response which your map get

play05:07

map post map put and so on are going to

play05:10

use now this is all fine but first

play05:13

exceptions are incredibly painfully slow

play05:17

and using them like this to control the

play05:19

flow of the application is in my opinion

play05:21

a very very bad practice and this is

play05:23

actually a very common criticism of

play05:25

exceptions used in that way and of

play05:27

course you have the nullability aspect

play05:29

which isn't actually really enforceable

play05:32

because even if you enforce them in your

play05:33

own assembly whoever else is consuming

play05:36

it doesn't have to actually use them and

play05:38

that's where option and result come in

play05:41

to fix both problems but before I show

play05:43

you that I want to let you know we just

play05:45

released two brand new costs on Dom

play05:46

train call from Zero to Hero git and

play05:49

GitHub actions those two costes are

play05:51

authored by Scott sober who has

play05:52

incredible experience in both topics and

play05:55

they both take you from knowing nothing

play05:56

about the topic into some really really

play05:58

advanced stuff and what I loved about

play05:59

those courses is that they both show you

play06:01

what happens when things go wrong and

play06:03

how to fix it now until the 18th of

play06:05

August you can buy the GitHub actions

play06:06

course and you will get the git course

play06:08

for free Link in the description now

play06:10

back to the video so what is that result

play06:12

and option type well I'm going to go on

play06:14

new and I'm going to install language

play06:17

extensions. core and you get package

play06:19

called language extensions. core why

play06:21

because that language extension package

play06:24

adds functional element and those two

play06:26

types are technically monuts even though

play06:28

I'm not going to actually talk about

play06:29

bonage in this video but they add those

play06:32

types in the code base in at least one

play06:34

of their flavors now the way they're

play06:36

going to work in the r time when type

play06:38

unions are actually added is going to be

play06:40

a bit different because the experience

play06:42

of consuming them will be way way more

play06:44

elegant but I'm going to try and show

play06:46

you the idea behind them so let's say I

play06:49

wanted to change this to use one of

play06:51

those two types well what you would use

play06:53

here is you would use an option type so

play06:56

you would say not user but this returns

play06:59

an option of type user what is an option

play07:03

well an option is a discriminated Union

play07:05

type which can have one of two states it

play07:08

can be some value of a specific type or

play07:11

it can be no value conceptually you can

play07:14

think of it a bit like nullable type of

play07:18

type T but the difference is and we see

play07:20

that with both cases that in order to

play07:22

lead to safer code you have to handle

play07:26

both outcomes there's no way to try to

play07:28

get out of handling both both scenarios

play07:30

and that's the whole idea of

play07:32

discriminated unions and type unions you

play07:34

have to deal with both scenarios or as

play07:37

many scenarios as there are the other

play07:40

thing is the user this user can have

play07:42

different outcomes it can have

play07:44

successful States and fail States well

play07:46

what you need to have here is not a user

play07:48

but a result of type user and as you can

play07:52

see by the way here nothing really

play07:53

changed this is exactly the same from

play07:56

the way I implemented this this returns

play07:58

a nullable type over here but that is

play08:01

automatically converted into an option

play08:03

through an implicit conversion and

play08:05

that's the same for the result over here

play08:08

in the user I didn't have to say new

play08:10

result of type user the user is

play08:12

converted into a result automatically

play08:14

however the exceptions now are turned

play08:17

into what we call errors in a result

play08:20

because a result can be either a success

play08:24

or an error a failure basically and the

play08:26

failure is represented in this

play08:28

implementation using an exception and in

play08:30

order to change my code base to accept

play08:32

this I'm going to say uh return new

play08:35

result of type user but we actually

play08:37

return an exception which automatically

play08:39

turns it into a failure and I'm going to

play08:42

do the same here return new exception

play08:46

and that's it so now I have my user

play08:50

service changed and I know many people

play08:52

already hate this because now they're

play08:54

like okay why am I returning this but

play08:56

trust me you will write safer code by

play08:59

doing this now let's change the existing

play09:02

code we had to use these new types and

play09:04

I'm going to start with the nullable

play09:06

aspect of it as you can see now I can no

play09:08

longer say this is a user because it's

play09:11

not a user it is actually an option of

play09:14

type user okay but if I turn it into an

play09:17

option I can no longer use the name how

play09:20

am I supposed to get the properties of

play09:24

that user well you can't what you can do

play09:27

is you can try to switch on that object

play09:31

and handle both of these situations you

play09:33

are going to have to exhaustively handle

play09:36

both scenarios now there's many methods

play09:39

that you can use here but the one we

play09:41

actually are going to get in cop is

play09:43

you'll be able to switch on this and say

play09:47

that if this is some value do this if

play09:49

this is nonone value do this the way

play09:52

language extension does it is we have to

play09:54

say match and we have to match both

play09:57

scenarios so if we have some value then

play10:00

we have a user and if we have none value

play10:05

then we have nothing and we need to

play10:07

handle both situations now this looks

play10:09

ugly because it doesn't really fit the

play10:10

language in its current state but

play10:12

eventually this will be a switch and

play10:14

it's going to be pretty but what this

play10:16

means is I can go ahead and say that hey

play10:19

if a user exists then now I can access

play10:23

the user object and I can say name over

play10:26

here and the name is this or the the

play10:29

user did not exist and that's it and now

play10:32

if I was to run this as it is of course

play10:35

we are creating the user here so that

play10:38

should not be a problem I should just

play10:40

see the name is whatever so my code will

play10:43

work exactly the same however if I try

play10:46

to get a user that did not exist so Nick

play10:48

a then as you're going to see the user

play10:50

did not exist I'm handling both

play10:53

situations very very elegantly now of

play10:55

course this would be way prettier if we

play10:58

had the switch like I said because that

play11:00

then would be you is this and you'll be

play11:04

able to handle it like something like

play11:06

this and if it was none you would have

play11:11

something like this so you'd be way

play11:12

closer to this experience than any other

play11:16

experience and I think this looks pretty

play11:19

pretty nice now option types also have

play11:22

different approaches to this you can

play11:23

actually say that user option do if some

play11:27

then you can have just the happy path

play11:30

handling so you can say hey if there is

play11:32

some value then do this this is way way

play11:36

more functional that what most people

play11:38

are used to in cop but you can do that

play11:41

and then you can also do the same with

play11:43

if none we don't really know if those

play11:45

things are going to be added in cop but

play11:47

those are things that you can do

play11:49

currently with language extensions if I

play11:50

just revert to the previous state with

play11:53

the match then I'd be able to show you

play11:55

what's now happening with this approach

play11:58

so now we no longer have have all that

play12:00

going on if I want to get an ey result I

play12:02

don't need a method to begin with I can

play12:04

simply say that user service. create

play12:06

user and actually I'm going to turn this

play12:09

into just the result and remember this

play12:12

type explicitly is result of type user

play12:14

and that can only be success or failure

play12:17

so I can say I result of HTTP response

play12:22

equals result. match same thing I can

play12:25

also map but I'm going to use match here

play12:28

so if it is a success I'm going to say

play12:32

return results. okay with that user if

play12:37

it is an exception or a failure I'm

play12:40

going to say results. bad request and

play12:44

then exception do message here we go

play12:48

very very easy very very simple this can

play12:50

actually become even prettier here you

play12:52

go and now I have all that code turned

play12:55

into this and I do handle both

play12:58

situations I have to say that if

play13:00

something is handled correctly here's my

play13:02

response if something is not handled

play13:04

correctly then that's how I deal with

play13:06

this and I don't have to throw

play13:07

exceptions I can just propagate that if

play13:10

I don't need to use it or I can simply

play13:12

handle it when I need to handle it I

play13:14

think this is a great addition to cop I

play13:17

really really hope this is being added

play13:19

and I do want to take a look at the Q&A

play13:20

because Microsoft has addressed this in

play13:22

the proposal so why do I need the option

play13:24

type if I can do the same thing with n

play13:26

and La reference types well the answer

play13:28

is you might not need the option type at

play13:30

all if you're comfortable with using NS

play13:32

and for the same purpose however some

play13:33

developers prefer the option types as it

play13:36

has stronger enforcement it is all about

play13:39

enforcing the usage and the consumption

play13:42

through option it's not something you

play13:44

can turn off you have to handle both

play13:46

things that's where the power is coming

play13:48

from and then again why do I need the

play13:50

result type when I already have

play13:51

exception handling many of the use cases

play13:53

for the result type are solved by using

play13:55

extension handling is C however you may

play13:57

prefer to avoid extension handl which by

play13:59

the way is very expensive in terms of

play14:01

performance and require the caller to

play14:02

deal with errors explicitly where errors

play14:05

unexpected and occur commonly at runtime

play14:08

it's a way better experience I've seen

play14:10

this used all the way back in 2017

play14:13

actually is when I first saw this result

play14:15

and option types used in a production

play14:17

code base when I first started work at a

play14:18

massive company here in the UK and they

play14:21

work they do actually lead to a better

play14:24

code base they do have quite a bit of a

play14:27

steep ask to start using them and

play14:29

understand them and because they're not

play14:31

part of the runtime there is a level of

play14:32

training you need to do for your

play14:34

developers to actually understand them

play14:36

but once they do they work wonders and

play14:38

then again in the end if we have them

play14:41

already in third party libraries why are

play14:42

they being added were they being added

play14:44

because we want to standardize them and

play14:46

have interoperability between libraries

play14:48

I think it's a great feature I'm so so

play14:50

happy Microsoft is adding it but I do

play14:52

want to know from you what do you think

play14:53

about this please leave a comment down

play14:55

below and let me know and Microsoft is

play14:56

watching these videos so any feedback

play14:58

you have will also be taken into account

play15:00

by Microsoft well that's all I had for

play15:01

you for this video thank you very much

play15:02

for watching and as always keep coding

Rate This
β˜…
β˜…
β˜…
β˜…
β˜…

5.0 / 5 (0 votes)

Related Tags
C# TutorialDiscriminated UnionsType UnionsNullable TypesError HandlingCode SafetySoftware DevelopmentProgramming Best PracticesMicrosoft Q&ACoding Standards