Showing posts with label language. Show all posts
Showing posts with label language. Show all posts

Thursday, November 08, 2018

Using configuration records and operators to reduce number of overloaded methods

When writing libraries you sometimes want to provide users (that is, programmers) with a flexible API. If a specific part of your library can be used in different ways, you may want to provide multiple overloaded methods accepting different combinations of parameters.

For example, IOmniPipeline interface from OmniThreadLibrary implements three overloaded Stage functions.

function  Stage(pipelineStage: TPipelineSimpleStageDelegate; 
taskConfig: IOmniTaskConfig = nil): IOmniPipeline; overload;
function  Stage(pipelineStage: TPipelineStageDelegate; 
taskConfig: IOmniTaskConfig = nil): IOmniPipeline; overload;
function  Stage(pipelineStage: TPipelineStageDelegateEx; 
taskConfig: IOmniTaskConfig = nil): IOmniPipeline; overload;

Delphi’s own System.Threading is even worse. In class TParallel, for example, there are 32 overloads of the &For class function. Thirty two! Not only it is hard to select appropriate function; it is also hard to decode something useful from the code completion tip. Check the image below – can you tell which overloaded version I’m trying to call? Me neither!

overloads1

Because of all that, it is usually good to minimize number of overloaded methods. We can do some work by adding default parameters, but sometimes this doesn’t help. Today I’d like to present an alternative solution – configuration records and operator overloading. To simplify things, I’ll present a mostly made-up problem. You can download it from github.

Friday, September 06, 2013

The Delphi Language is Lagging Behind

While I love the way Delphi is expanding to multiple platforms (but please, guys, stop the horses once in a while and fix some bugs!), I think the Delphi language could use a facelift. In particular, I like what Eric is doing with the DWS and I absolutely enjoy coding in Smart Mobile Studio where I could use the full power of Eric’s improvements to the language.

I absolutely love DWS’ lambda statement. Delphi’s approach to anonymous methods is so damn long-winded (even for us Pascal programmers who love to type) that lots of programmers reject it just because of the unwieldy syntax. In DWS, however, I can use

var repeater := TW3EventRepeater.Create(lambda (Sender) => MyFunction, 5000);

or

ResizeChildren(FClientArea.Height, [TAlign.Top, TAlign.Bottom],
lambda (layout) => layout.GetConfig.GetHeight,
lambda (layout, value) layout.GetConfig.Height(value) end);

Much shorter. More readable.


Also useful are properties with anonymous storage. If my property only exposes a field and doesn’t use getter or setter, I can shorten the code by 50% by not defining the field at all. Instead of

type
TMyClass = class
private
FData: integer;
public
property Data: integer read FData write FData;
end;

I can write

type
TMyClass = class
public
property Data: integer;
end;

Another nice feature are property expressions – a way to write anonymous getters and setters.

type
TToDoListTemplate=class(TW3form)
public
property Task: string read (W3lblTask.Caption) write (W3lblTask.Caption);
property Detail: string read (W3lblDet.Caption) write (W3lblDet.Caption);
end;

These are incredible time saver. If I redo this the Delphi way, I end up with a much longer code.

type
TToDoListTemplate=class(TW3form)
private
function GetDetail: string;
function GetTask: string;
procedure SetDetail(value: string);
procedure SetTask(value: string);
public
property Task: string read GetTask write SetTask;
property Detail: string read GetDetail write SetDetail;
end;






function TToDoListTemplate.GetDetail: string;
begin
Result := W3lblDet.Caption;
end;


function TToDoListTemplate.GetTask: string;
begin
Result := W3lblTask.Caption;
end;


procedure TToDoListTemplate.SetDetail(value: string);
begin
W3lblDet.Caption := value;
end;


procedure TToDoListTemplate.SetTask(value: string);
begin
W3lblTask.Caption := value;
end;

I also love type inference and in-line variable declaration which allow me to write code like this:


var item := ActionList.Items[ActionList.Add] as TLBItem;
item.Header := header;
item.Text := text;


or


for var item in ActionList.Items do


When I have to write a multiline string constant, multiline strings come handy.


writeln("Hello
World");


Even better – DWS will ignore leading common indentation if string is introduced with #” (or #’).


writeln(#"
    Hello
    World");


And last (but definitely not least), DWS implements a true conditional operator.


s := if a = 0 then 0 else 1/a;