Option type is a polymorphic type introduced in Type theory and has been adopted in many programming languages most of them are functional languages like (F#, Haskell, typescript,…) but some are object-oriented too like java.
In CSharp there is no yet support for OPTION type as a native type but there is a very strong and robust 3rd party implementation using this NuGet.
What is Option Type?
Option type is a type that can have value or nothing, similar to nullable types that can be a value or null, but option type is not the value but a function of value.
if we try to express the difference between Nullabe and optional in a semi-formal notation it
Object X => X : Object||Null
Optional X => F(x):Object||Nothing
Real-World Example
public ReservationViewModel AddItemToReservation(string id,string itemId)
{
var reservation = \_restaurant.GetReservation(id);
if (reservation == null)
return null;
var item = \_inventory.GetItem(itemId);
if (item == null)
return null;
reservation.Items.Add(item);
return ReservationViewModel(reservation);
}
private ReservationViewModel MapReservationViewModel(Reservation reservation)
{
if (reservation == null)
return null;
var user = \_ClientsManger.GetUser(reservation.Id);
return new ReservationViewModel()
{
From = reservation.From,
User = user != null ? $"{user.FirstName} {user.SecondName}" : "Not a client",
Id = reservation.Id,
Name = reservation.Name,
To = reservation.To
};
}
The previous method gets a user reservation from a database and query for an inventory item and adds the inventory item to the reservation than call “MapReservationViewModel” to map the result to a reservation view model.
It is clear that we need to keep checking for null values and propagate nulls to prevent crashing so the first method check if the reservation is not Null and the Inventory Item is not null and the if so it just returns null and the second method has to ensure that the reservation it gets is not null otherwise it has to return null. And so on every method that will be in the chain should handle the null cases.
While you could imagine that it will be an easy task and that you can handle it with the help of the null propagation feature in c# or by manually checking, I would assure you that in complex cases you would end up with a lot of null reference exceptions.
The Functional Programming solution
I would start by doing a small refactor in the GetReservation, GetInventory, GetUserMethods to return option instead of nullable
public Optional.Option<Reservation> GetReservation(string id)
{
\_reservations.TryGetValue(id, out Reservation result);
return result.SomeNotNull();
}
Then in my AddItemToReservation method, I will join the result of GetReservation and GetInventoryItem
public Option<ReservationViewModel> AddItemToReservation(string id, string itemId)
{
var reservationOptinal = \_restaurant.GetReservation(id);
var itemOptional = \_inventory.GetItem(itemId);
var result= reservationOptinal.Intersect(itemOptional)
The intersection of 2 optional will return an optional tuple of both iff both has values and Nothing if any of them has nothing. ( AND in logic operators).
now I will start to apply my value transformations using the select operator
public Option<ReservationViewModel> AddItemToReservation(string id, string itemId)
{
var result= reservationOptinal.Intersect(itemOptional)
.Select(AddItemFunc);
Reservation AddItemFunc(( Reservation , Item ) tuple)
{
var (reservation, item) = tuple;
reservation.Items.Add(item);
return reservation;
}
}
AddItemFunc is an inner function that receives a value tuple of (reservation, item) and adds the item to the reservation and returns the reservation.
now we will only need to build another optional value type by making a union of reservation and OptionalUser and that can be done easily using the select operator
.Select(reservation => (reservation, \_ClientsManger.GetUser(reservation.UserId)))
Now the full method will be :
I think it is readability is acceptable and it is very computational efficient as at any time if the option becomes Nothing due to any transformation the method will directly return Nothing without continuing the computation.
Final note the Intersect method is not yet included in the Optional library so you can find it here :
https://gist.github.com/TheFo2sh/121c208a6e1bb235df32e43d51b89c68
Top comments (0)