I have considered using Option .
This means function conversion, for example:
Customer GetCustomerById(Int32 customerID) {...} Customer c = GetCustomerById(619); DoStuff(c.FirstName, c.LastName);
to return the Maybe parameter:
Maybe<Customer> GetCustomerById(Int32 customerID) {...}
in my non-functional language, then I need to check if the return value is present:
Maybe<Customer> c = GetCustomerById(619); if (c.HasValue) DoStuff(c.Value.FirstName, c.Value.LastName);
And this works quite well:
- you know from the function signature whether it can return
null (and not to throw an exception) - you are trying to check the return value before using it blindly
But no junk collection
But I'm not in C #, Java or C ++ with my RAII. I am in Delphi; native language with manual memory management. I will continue to show code examples in C #.
With manual memory management, my original code is:
Customer c = GetCustomerById(619); if (c != nil) { try { DoStuff(c.FirstName, c.LastName); } finally { c.Free(); } }
converts to something like:
Maybe<Customer> c = GetCustomerById(619); if (c.HasValue) { try { DoStuff(c.Value.FirstName, c.Value.LastName); } finally { c.Value.Free(); } }
Now I have Maybe<> containing the link is invalid ; it's worse than zero because Maybe now thinks it has valid content and the contents has a pointer to memory, but that memory is not valid.
I exchanged a possible NullReferenceException for a random data failure error.
Has anyone thought about this problem and how it works?
Add. Free for Maybe
I thought about adding a method to a structure called Free :
void Free() { if (this.HasValue()) { _hasValue = false; T oldValue = _value; _value = null; oldValue.Free(); } }
What works if people call him; and know to call it; Know why to call it; and know that they should not call.
A lot of subtle knowledge to avoid the dangerous mistake that I just introduced when trying to use the type of option.
It also falls apart when an object wrapped in Maybe<T> is actually destroyed indirectly by a method not called canonical Free :
Maybe<ListItem> item = GetTheListItem(); if item.HasValue then begin DoStuffWithItem(item.Value); item.Value.Delete; //item still thinks it valid, but is not item.Value.Selected := False; end;
Bonus Chat
The Nullable / Maybe / Option has the advantage of working with types that do not have a built-in non-value (for example, records, integers, strings where there is no built-in non-value).
If the function returns a value other than zero, then there is no way to report the non-existence of the return result without using any special sentinal values.
function GetBirthDate(): TDateTime; //returns 0 if there is no birth date function GetAge(): Cardinal; //returns 4294967295 if there is no age function GetSpouseName: string; //returns empty string if there is no spouse name
This option is used to prevent special values ββof the sentinel value and tells the caller what is really happening.
function GetBirthDate(): Maybe<TDateTime>; function GetAge(): Maybe<Integer>; function GetSpouseName: Maybe<string>;
Not only for types with zero value
The Option type also gained popularity in order to avoid NullReferenceExceptions (or EAccessViolation at $ 00000000), dividing the thing into nothing .
Functions that return special, sometimes dangerous, issued values
function GetBirthDate(): TDateTime; //returns 0 if there is no birth date function GetAge(): Cardinal; //returns 4294967295 if there is no age function GetSpouseName: string; //returns empty string if there is no spouse name function GetCustomer: TCustomer; //returns nil if there is no customer
They are converted into forms where special, sometimes dangerous, brave meanings are impossible:
function GetBirthDate(): Maybe<TDateTime>; function GetAge(): Maybe<Integer>; function GetSpouseName: Maybe<string>; function GetCustomer: Maybe<TCustomer>;
Subscribers are created in order to implement a function that may not be returned by a thing , and they must go through the existence test hoop. In the case of types that already support null , Option gives us a chance to try to stop people from throwing NullReference exceptions.
In functional programming languages, it is much more durable; the type of the return value can be constructed so that it is impossible to return nil - the compiler simply does not allow it.
In procedural programming languages, the best we can do is block nil and make achievement impossible. And in this process, the caller has more reliable code.
You can argue "why not tell the developer that he never made mistakes":
Bad
customer = GetCustomer(); Print(customer.FirstName);
Good
customer = GetCustomer(); if Assigned(customer) Print(customer.FirstName);
Just get it.
The problem is that I want the compiler discovered these errors. I want errors to happen first. I want success. This makes the caller realize that the function may fail. The signature itself explains what to do, and makes it easier to deal with it.
In this case, we implicitly return two values:
- customer
- a flag indicating whether the client is really there
People in functional programming languages ββhave adopted a concept, and this is a concept that people are trying to return to procedural languages, that you have a new type that conveys if the value is there or not. And attempts to blindly use it will give a compile-time error:
customer = GetCustomer(); Print(customer.FirstName); //syntax error: Unknown property or method "FirstName"
Reading bonuses
If you want to know more about trying to use the Maybe functional monad in procedural languages, you can consult other thoughts on this: