- edited description
A TMaybe Class for Spring4D
I humbly offer the following two classes for inclusion in the Spring framework. I am aware that this can very likely be improved by people smarter than me.
I split it into two classes because I couldn't find a way to make a single class work for both primitives and classes.
The idea is that in order to really encapsulate something, you have to have a "safe" API. The TMaybe classes enable you to always return *something", instead of nil or empty or "nothing", The idea is that the return value will have either one valid item or the default value for the type.
It helps ensure that nil is never returned as a function result.
This comes from Mark Seemann, or at least his PluralSight course was the first place I have ever seen it used.
Thoughts?
unit Spring.Maybe;
interface
uses
Spring
, Spring.Collections
;
type
TPrimitiveMaybe<T> = class(TInterfacedObject, IEnumerable<T>)
private
FList: IList<T>;
function GetValue: T;
property Item: IList<T> read FList implements IEnumerable<T>;
public
constructor Create; overload;
constructor Create(aT: T); overload;
property Value: T read GetValue;
end;
TClassMaybe<T: class, constructor> = class(TInterfacedObject, IEnumerable<T>)
private
FList: IList<T>;
function GetValue: T;
property Item: IList<T> read FList implements IEnumerable<T>;
public
constructor Create; overload;
constructor Create(aT: T); overload;
property Value: T read GetValue;
end;
implementation
{ TBaseTypeMaybe<T> }
constructor TPrimitiveMaybe<T>.Create;
begin
inherited Create;
FList := TCollections.CreateList<T>;
end;
constructor TPrimitiveMaybe<T>.Create(aT: T);
begin
inherited Create;
FList := TCollections.CreateList<T>;
FList.Add(aT);
end;
function TPrimitiveMaybe<T>.GetValue: T;
begin
Result := FList.SingleOrDefault
end;
{ TClassMaybe<T> }
constructor TClassMaybe<T>.Create;
begin
inherited Create;
FList := TCollections.CreateList<T>;
FList.Add(T.Create);
end;
constructor TClassMaybe<T>.Create(aT: T);
begin
Guard.CheckNotNull(aT, 'aT');
inherited Create;
FList := TCollections.CreateList<T>;
FList.Add(aT);
end;
function TClassMaybe<T>.GetValue: T;
begin
Result := FList.SingleOrDefault;
end;
end.
Comments (8)
-
reporter -
repo owner Can you also post some code that would show it's use? Because after reading a couple of articles about the maybe monad in other languages I feel that the code would not get any cleaner but just more obscure and less understandable. Without proper extension methods in Delphi this would be even worse I think.
-
reporter The idea is to provide a type that can never return nil. Now that I think about it, that makes the TPrimitiveClass<T> almost useless, because none of Delphi's primitives can be nil.
TClassMaybe will only work for classes with default, parameterless constructors. Not all Delphi classes have that.
The idea is that instead of
TMyClass = class function GetMyWidget: TMyWidget; end;
you'd have
TMyClass = class function GetMyWidget: TClassMaybe<TMyWidget>; end;
Where you'd guarantee that the return value of GetMyWidget would never be nil. But the class would have to have the default constructor, etc.
In the end, after further consideration, consider this withdrawn. It is valuable in the .Net and Java worlds where, say, a string can be null, but in Delphi, it just doesn't work after all.
-
repo owner It's also more valuable in languages that have dynamic typing and embrace functional programming more than Delphi does. Also when we think about being nil safe Delphi offers something that C# does not (without any judgement if that is good or bad). You can call a method on a nil reference whereas in C# you get an NPE.
So in fact you can write code like this:
var street: string; begin street := person.Address.Street
where person or its Address property are nil by writing getters like these:
function TPerson.GetAddress: TAddress; begin if Assigned(Self) then Result := fAddress else Result := nil; end; function TAddress.GetStreet: string; begin if Assigned(Self) then Result := fStreet else Result := ''; end;
P.S.: Fun fact: C# 6.0 will have this - called Null propagation
-
reporter Yeah -- the idea was to ensure that a function (query) returns something and not nil. But since Delphi isn't rooted, it already defaults most types and thus makes TMaybe redundant.
Interesting thought experiment at least. :-)
-
repo owner - changed status to wontfix
-
Option types ( another name of MayBe types ) are usually seen as more consistent concept neighboring Nullable concept. http://stackoverflow.com/questions/11146825/whats-the-difference-between-an-option-type-and-a-nullable-type
But since Spring4D already has Nullable - then adding Option types maybe would add some confusion ( two tools for the same job) for small benefit ( Option types are somewhat better than Nullables, but - somewhat)
-
repo owner From a functional perspective it is correct - however nullable types as in C# or Spring4D are to add the state of null to a value type - hence the difference to the option type.
Reference types already have the state of null/nil (which causes other problems as we all know). These problems usually are being solved via option types in functional programming because they offer pattern matching and checking if something is some or none in a rather convenient way.
- Log in to comment