Monday, January 25, 2010

Reflections: A Great Programming Language, 3 lessons

As I mentioned earlier, programming enjoyableness has little to do with the programming language.  In this article, I will give 3 lessons on what makes a great programming language.

Lesson 1

First is a notion of what the goal for a great programming language is, that is, a great, not simply a good, programming language.  A great programming language knows that the programmer will want to express things whatever way they want.  Ok, that's nice.  So, what?  Well, we must discuss some things.

First, the notion of turing equivalence.  What is turing equivalence? It's a phenomenon first recognized by a man named Alan Turing.  Turing equivalence shows that if a programming language can be simulated by a special machine called a turing machine, i.e. a turing-equivalent language, then, the language can be simulated by all other turing-equivalent programming languages.  So, this puts all turing-equivalent languages on an even playing field in their abilities.  Fortunately for us, pretty much every programming language used in existence today, is a turing-equivalent language.  As an example, we could write a C++ interpreter in JavaScript (speed is irrelevant to equivalence), or vice versa.

So, what's an exercise in turing equivalence?  It is simply when we implement the facilities of one language's features in another language.  For example, if we were to implement immutable data structures in C++, or if we were to implement formal function parameters in Forth.  Such exercises in turing equivalence are considered exploitable, i.e. they provide useful, leverageable functionality in programming--this, as opposed to not generally useful functionality.  So to review, there are many useful language features, and a great programming language will let us express code the way we want i.e. using whatever language features we would want.  Therefore, the goal follows:

The goal: to optimally facillitate exploitable exercises in turing equivalence.

Lesson 2

Let's consider a lesson from practica.  Some people recognize bad code design, and in their naivete, they want to somehow force the programmer to use good code design, but this is impossible.  Bad programmers will write bad code in any language.  And how are they going to learn to write good code if they're not allowed to learn by shooting themselves in the foot?

I hear all the time about people who want to put arbitrary limits on code structure! This will never work! I hear about limiting the number of methods in a class, or disallowing global variables, or other such unnatural, arbitrary nonsense. The truth is, the programmer must learn how to design good code, and if they're a competent programmer, then whatever design choice they make is competent, and we have no business getting in their way!  So, what is the lesson learned?

Don't design a PL for bad programmers.

A language designed for bad programmers is a burden on competent programmers.  Such a language will create a vicious cycle, always attracting bad programmers to the language.  The perfect programmer knows what they want. A good programmer can figure out what they want. We should always assume a competent (perfect or good) programmer, because programmers are able to improve themselves to become at least good!

Lesson 3

After establishing lesson 2, that we can focus on competent programmers, we soon realize the third lesson:

A great programming language simply stays out of the competent programmer's way.

So now the question becomes: how do we stay out of the programmer's way?  There are only 2 ways to stay out of the programmer's way:

Method #1: By already providing the programmer's desired language features.

This removes implementation detours out of the programmer's way. This would be nice, but it will never realistically always happen for all programmers, for all their uses of the language.

The second method is not such a direct approach, but we will see it's better:

Method #2: By providing the capabilities to bootstrap the features desired by the programmer.

This, while obviously not as convenient as the first option (some implementation detours are required at times), is much more scalable: the language is guaranteed to never absolutely get in the way.

The perfect programmer knows what they want. A good programmer can figure out what they want. The first method never helped anyone get exactly what they want.  A great programming language will never tie the hands of a perfect or good programmer, so method 2 is the only possibility.

Friday, January 22, 2010

The Great Programmer

It seems like all programmers want to find a perfect language for them. Now, don't get me wrong -- I want a perfect language as well, but we all know there never will be such a thing, and the truth is, programming is not about having a perfect programming language that does stuff for you just by telling it what to do in plain english. If such were the case, we wouldn't need programmers.

So now what? Are we forever doomed to hate what we do? Well, actually there is hope: the key to enjoyable programming is in having a great programming experience. Experience? What does that mean? It means that we feel like we've solved problems, invested in future productivity, and/or made things better in a session of programming, while not stalling our progress towards the development of the software we are programming.

As we can see, this hasn't alot to do with the language we're using. And this brings me to what a great programmer is. You know the old saying that a bad programmer can write bad code in any language? Well, the great programmer is one at the opposite end of that spectrum:

A great programmer produces great code in any language.

Yep, that's right. A great programmer is one who has gotten so good at coding, that they have risen above the mire, the style that their everyday language nudges them towards, and they have found those good architectural and semantic patterns that make for great code, and then they can write great code in any language!

How do you recognize a great programmer?
  • They tend to design reusable libraries, routines, and code, in general. Instead of writing 1,000-line algorithms, they develop a 1,200-line reusable, well-designed library, and simply reuse it when they need it again.
  • They don't need to rely on comments: they read the code itself.
  • They limit the amount of architectural aerobatics! Instead of spending 3 months creating a scripting programming language in the programming language, they spend a week creating an object-oriented system that is not much harder to deal with than a scripting language.
  • They limit complexity! Instead of coding a perfect routine that takes 3 hours to write and 2 days to debug, they write a very simple, good-enough routine that takes 5 minutes to write, and doesn't have bugs.
  • They seem to have ingrained habits of how to write good code and make good design choices. They've been here before, and they know that design X will easily solve problem Y.
  • Their work is excellent in design. They know that abstraction means well-designed, flexible libraries.
  • They optimize only 1% of the time, but when they do optimize, those optimizations produce durastic improvement.
  • They code defensively. They attempt to ingrain air-tight error prevention into their libraries, so that nobody can use them incorrectly without knowing about it. They seek to catch errors on program start-up where possible. In addition, they exploit the whatever type checker is available to prevent errors at compile-time when possible.
  • They test the functionality immediately, to make sure it works. Coding a feature means both writing it, and ensuring that it works correctly.
So, in summary, a great programmer goes for reusability, streamlining, simple, good-enough, anti-optimization, defensive coding, immediate testing.

Tuesday, December 8, 2009

Programming Recipes: Dataflow graph!


Yay! Now I can get down to one of the most useful of all recipes thus far! It's called a dataflow graph, or at least, that's what I call it.

Motivation

So, it's just another typical day at company XYZ, and you're writing some UI code. It's getting a bit hairy, because you have to keep track of updating things on the UI according to changes made to values...
procedure TfrmMain.seAmountChanged(Sender: TObject);
begin
  HandleAmountChanged;
end;

procedure TfrmMain.HandleAmountChanged;
begin
  UpdateSubtotals;
end;

function TfrmMain.GetSubTotal: extended;
begin
  Result := GetProductValue + GetOptionValue;
end;

procedure TfrmMain.UpdateSubTotals;
begin
  lblSubtotal1.Caption := Format('%.2m',[Subtotal]);
  lblSubtotal2.Caption := Format('%.2m',[Subtotal + ShippingTotal]);

  UpdateGrandTotal;
end;

procedure TfrmMain.HandleShippingFlagChanged;
begin
  UpdateGrandTotal;
  UpdateChart;
end;

Yeah, well, you get the idea. Your form is full of values that need to update as the user makes changes or whatnot. Man! All these HandleXXXChanged() and UpdateXXX() procedures are getting complicated! Plus, sometimes different changes require updating values in common, and a value gets updated more than once! That's inefficient! Now I have to add a ton of boolean fields to the form to avoid that, not to mention working out the coordination of the flags with procedure calls...oh, this could be a nightmare!

Or could it be?

There's so much common, repetitive structure to this system. Surely, we could automate something here. As a matter of fact, we can!

Brainstorming

So, what's the deal? Hmm... We have a set of values; each must auto-update when any changes to values that it depends on -- its dependancy values -- occur. Also, we don't want any of the auto-updated values to be computed multiple times, because that's inefficient.

So, we must traverse through a series of updates, preserving the updating order to always update the dependancy values before any of their dependant values are updated. This will allow us to only have to update each single value once.

How will we keep track of dependancies and dependants? We'll have to specify them, since we don't have code reflection for expressions, which would have allowed us to auto-derive the dependancies from expressions. Also, we will want to specify the "formula" for each value only once in the code -- NO duplication, nor any copy and paste. This will avoid inconsistencies and will reduce code size. The best place to specify dependancies is right next to the formula, for easy manual "bookeeping" and checking.

Discussion

So, basically, we have a collection of values that we must always keep consistent with a "formula" that defines them. When a dependancy changes, it's dependant values, to keep consistent with their formulas, will recompute, regarding their formulas as realtime-enforced constraints. So, it's like the new values -- data -- flows, from dependancies, to dependants, automatically. This is called the "dataflow paradigm" (see Wikipedia's article).

And because dependancies can be reused by multiple dependants, the dataflow network structure, rather than forming a tree that branches from dependants to dependancies, forms a graph -- a directed, acyclic graph (no circular references in any formulas). Thus we call this system a "dataflow graph" that we'll design and leverage.

Herein is the abstraction that dataflow graphs provide: Instead of it feeling like we're manually assigning the value to the formula, it feels like we specify the formula for a value only once in the code, and the value automatically keeps itself consistent with its formula, over time!

This should be what we want! A system for keeping track of updating for us!

Getting to Work

First, we start with our declarations:
unit uDG;

interface

type
  //index to a node:
  INode = integer;
...


Now, we want the formula for a value to be close to where we specify the dependancies for it, so we're going to use callbacks:
...
  TNodeAction = (naReadDependancies, naUpdate);

  //manages the implementation of a single node
  //in the dataflow graph:
  TNode = class;

  //our callback for nodes:
  TNodeHandler = procedure(ANode: TNode; AAction: TNodeAction) of object;

To construct our dataflow graph, we're going to have to describe the properties of each node:
...
  //description of a node:
  TNodeDesc = record
    ID: INode;
    EagerlyUpdated: boolean;
    Handler: TNodeHandler;
  end;

And now, the declaration for the TNode Class:
...
  TNode = class
  private
    //reference to its parent TNodes container:
    FList: TNodes;

    //The node's index-ID:
    FID: INode;

    //Maintained lists for all dependancy and dependant values
    //of a node (for efficiency's sake):
    FDependants,
    FDependancies: TINodeSet;

    //The callback:
    FHandler: TNodeHandler;

    //whether is ok to call the ReadDependancies() method of this object:
    FCanReadDependancies,

    //Whether the node, and its dependants
    //should update eagerly when invalidated:
    FEagerlyUpdated,

    //Whether the node is currently in need of being updated:
    FValid: boolean;

    //helper functions as follows...

    function AllDependanciesValid: boolean;

    procedure EnsureInvalidated;
    procedure EnsureDependantsInvalidated;

    procedure EnsureDependantsUpdated;
  public
    constructor Create(AList: TNodes; ADesc: TNodeDesc);
    destructor Destroy; override;

    { we call this function ONLY in the callback, ONLY when asked to get
      a node's dependancies; This procedure can only be called in a callback,
      when the callback is asked to provide dependancies;  if called out of
      context, a runtime error will be thrown (to prevent silent bugs!): }
    procedure ReadDependancies(ADependancies: array of INode);

    { we call the function below after assigning to a variable that has no
      dependancies; if it has dependancies, this function will be called
      automatically: }
    procedure HandleSet;
      { outside of transactions, it performs eager invalidation of dependants,
        followed by eager update of them.

        inside transactions: Performs eager invalidation of dependants,
        deferring updates to the end of the transaction (useful when assigning
        values to multiple independant nodes at once) }

    { the following function performs lazy update (when your nodes are not
      updated eagerly, call this function to update a dependant node, and it
      will automatically update any dependants that need to be, all the way up
      the chain; Warning: do not call inside a transaction! }
    procedure EnsureUpdated;
  end;

And now, the TNodes class -- the container for each TNode:
...
  TNodes = class
  private
    //used to allow recursive transactions:
    FTransactionCount: integer;

    //determines whether to call the callback to update nodes that
    //have no dependancies:
    FComputeIndependantNodes: boolean;

    //the list of nodes:
    FItems: array of TNode;

    //Helper functions...

    function GetItem(ID: INode): TNode;
    function GetNItems: integer;

    procedure Compute(ID: INode);
  public
    constructor Create(AComputeIndependantNodes: boolean;
      Nodes: array of TNodeDesc);

    destructor Destroy; override;

    { functions for doing transactions i.e. assignments to multiple
      independant nodes, so that we're not recomputing dependants for
      each single assignment: }
    procedure BeginTransaction;
    procedure EndTransaction;

    property Items[ID: INode]: TNode read GetItem;
    property NItems: integer read GetNItems;

    property ComputeIndependantNodes: boolean read FComputeIndependantNodes
      write FComputeIndependantNodes;
  end;

//This function is a DSL-like function we will use to specify the properties
//of a node, when we create the dataflow graph:
function Node(AID: INode; AHandler: TNodeHandler = nil;
  AEagerlyUpdated: boolean = True): TNodeDesc;

type
  //A helper class that allows us to use strings in place of integers,
  //for nodeIDs:
  TNodeNames = class
  private
    FNodeIDs: array of string;

    function GetNodeID(AID: string; var AINode: integer): boolean;
  public
    constructor Create;
    destructor Destroy; override;

    //call this function when referring to a string ID that is "new" i.e. when
    //we define the dataflow graph):
    function NewNodeID(AID: string): INode;

    { call this function to refer to an already-existing string ID.
      This function will throw a runtime error if the ID passed is not found
      (good for catching mis-spelled string IDs): }
    function NodeID(AID: string): INode;
  end;

So, there you have it -- the declarations! Now, let's peek under the hood and look at a couple details:
constructor TNode.Create(AList: TNodes; ADesc: TNodeDesc);
begin
  inherited Create;
  ...
  { in the TNode constructor, we call the handler to read its dependancies.
    No handler assumes no dependancies; the FCanReadDependancies flag allows
    us to enforce that no calls can be made to TNode.ReadDependancies(),
    except at this point: }
  if Assigned(FHandler) then
    begin
      FCanReadDependancies := True;
      FHandler(Self, naReadDependancies);
    end;
  FCanReadDependancies := False;
end;

Take a look at these next two routines:
procedure TNode.EnsureInvalidated;
begin
  //only invalidate if already valid
  if not FValid then Exit;

  FValid := False;
  EnsureDependantsInvalidated;
end;

procedure TNode.EnsureDependantsInvalidated;
var
  iDependant: integer;
begin
  for iDependant := Low(FDependants) 
   to High(FDependants) do
     FList.Items[FDependants[iDependant]].EnsureInvalidated;
end;

As we can see, invalidating a node will recursively traverse down to its dependants, and its dependant's dependants, etc.
//when a node has been set,
procedure TNode.HandleSet;
begin
  //it's now valid
  FValid := True;
  
  //but now all dependants need to be updated  
  EnsureDependantsInvalidated;

  { after invalidating all dependants, now we
    update all dependants, but only if we're not
    inside a transaction, which will do this for us! }
  if FList.FTransactionCount = 0 then
    EnsureDependantsUpdated;
end;

Notice the next routine:
procedure TNode.EnsureDependantsUpdated;
var
  iDependant: integer;
begin
  for iDependant := Low(FDependants) to High(FDependants) do
    if FList.Items[FDependants[iDependant]].FEagerlyUpdated then
        FList.Items[FDependants[iDependant]].EnsureUpdated;

  for iDependant := Low(FDependants) to High(FDependants) do
      if FList.Items[FDependants[iDependant]].FEagerlyUpdated then
          FList.Items[FDependants[iDependant]].EnsureDependantsUpdated;    
end;

So, why don't we just use the same recursion for updating dependants as we do for invalidating dependants? Because, in order to avoid updating dependants before ALL their dependancies are ready to use, we must perform a breadth-first traversal of nodes. The other way would perform a depth-first traversal, resulting in some node down the chain trying to update itself with dependancies that we haven't traversed to and invalidated or updated yet.

Ok, last but not least:
procedure TNode.EnsureUpdated;
var
  iDependancy: integer;
begin
  { We mustn't call this procedure within a transaction; the library is
    guaranteed to never call this procedure in a transaction, so this assertion
    will only go off if the programmer using the library makes a mistake: }
  Assert(FList.FTransactionCount = 0);
  
  //only update a node if it's not yet updated:
  if FValid then Exit;

  //update each dependancy node: notice that we do a depth-first, recursive
  //traversal down to all dependancy nodes, with this:
  for iDependancy := Low(FDependancies) to High(FDependancies) do
    FList.Items[FDependancies[iDependancy]].EnsureUpdated;

  //handle calling the callback, which performs the actual
  //calculation of the node:
  FList.Compute(FID);

  //and now we're valid:
  FValid := True;
end;

That's all the implementation I'm going through -- the main core. One of my methodologies is that the quickest way to learn how to use a library is to see examples, or use-cases, of it, so now let's see what it looks like to USE this library!

Example

Below is a small-scale example -- the full listing of an example form, with comments:
unit Unit1;

interface

uses
  //Standard Delphi 4 Units:
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, Spin, Math,

  //Our dataflow graph library:
  uDG;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    Label2: TLabel;
    Label3: TLabel;
    SpinEdit1: TSpinEdit;

    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);

    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
    procedure SpinEdit1Change(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }

    //our DG library's data structures:
    FNodes: TNodes;
    FNodeNames: TNodeNames;

    //values of the form:
    FFlag: boolean;
    FLastMousePos: TPoint;

    //callbacks for each non-independant node:
    procedure HandleNode_CanvasPenWidth(ANode: TNode; AAction: TNodeAction);
    procedure HandleNode_Label1Caption(ANode: TNode; AAction: TNodeAction);
    procedure HandleNode_Label1Position(ANode: TNode; AAction: TNodeAction);
    procedure HandleNode_Label2Caption(ANode: TNode; AAction: TNodeAction);
    procedure HandleNode_Label3Caption(ANode: TNode; AAction: TNodeAction);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.HandleNode_CanvasPenWidth(ANode: TNode; AAction: TNodeAction);
begin
  case AAction of

    { time to get the dependancies for this node; note that you can only call
      the following method in a callback, when AAction is assigned the given
      value here: }
    naReadDependancies:ANode.ReadDependancies([
      FNodeNames.NodeID('SpinEdit1.Value')
    ]);

    { time to compute the value of this node; notice how the value for
      Canvas.Pen.Width depends on the value for SpinEdit1.Value; this is why
      we have SpinEdit1.Value in the call to ReadDependancies() above; our
      dataflow graph will automatically update dependant values when their
      dependancies are flagged as changed; in this case, the following code
      will be run: }
    naUpdate:Canvas.Pen.Width := Max(SpinEdit1.Value, 1);
  end;
end;

procedure TForm1.HandleNode_Label1Caption(ANode: TNode; AAction: TNodeAction);
begin
  case AAction of
    naReadDependancies:ANode.ReadDependancies([
      FNodeNames.NodeID('SpinEdit1.Value')
    ]);
    naUpdate:Label1.Caption := IntToStr(SpinEdit1.Value);
  end;
end;

procedure TForm1.HandleNode_Label1Position(ANode: TNode; AAction: TNodeAction);
begin
  case AAction of
    naReadDependancies:ANode.ReadDependancies([
      FNodeNames.NodeID('FLastMousePos'),
      FNodeNames.NodeID('Canvas.Pen.Width')
    ]);
    naUpdate:begin
      Label1.Left := Round(0.2*FLastMousePos.x + 0.8*Label1.Left);
      Label1.Top := Round(0.2*FLastMousePos.y + 0.8*Label1.Top);

      Canvas.LineTo(Label1.Left, Label1.Top);
    end;
  end;
end;

procedure TForm1.HandleNode_Label2Caption(ANode: TNode; AAction: TNodeAction);
begin
  case AAction of
    naReadDependancies:ANode.ReadDependancies([
      FNodeNames.NodeID('SpinEdit1.Value'),
      FNodeNames.NodeID('FFlag')
    ]);
    naUpdate:Label2.Caption := IntToStr(SpinEdit1.Value+Ord(FFlag));
  end;
end;

procedure TForm1.HandleNode_Label3Caption(ANode: TNode; AAction: TNodeAction);
begin
  case AAction of
    naReadDependancies:ANode.ReadDependancies([
      FNodeNames.NodeID('FFlag')
    ]);
    naUpdate:
      if FFlag then
        Label3.Caption := 'True' else
          Label3.Caption := 'False';
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  //create the TNodeNames helper class
  FNodeNames := TNodeNames.Create;

  //use it here; notice I'm calling its NewNodeID() method,
  //to "declare" new node IDs:
  with FNodeNames do
    FNodes := TNodes.Create(True, [
      Node(NewNodeID('FLastMousePos')),
      Node(NewNodeID('Canvas.Pen.Width'), HandleNode_CanvasPenWidth),
      Node(NewNodeID('Label1.Caption'), HandleNode_Label1Caption),
      Node(NewNodeID('Label1.Position'), HandleNode_Label1Position),
      Node(NewNodeID('Label2.Caption'), HandleNode_Label2Caption),
      Node(NewNodeID('Label3.Caption'), HandleNode_Label3Caption),
      Node(NewNodeID('FFlag')),
      Node(NewNodeID('SpinEdit1.Value'))
    ]);

  //the transaction to initialize all values:
  FNodes.BeginTransaction;
  FLastMousePos.x := 30;
  FLastMousePos.y := 30;
  FFlag := False;
  FNodes.EndTransaction;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FNodes.Free;
  FNodeNames.Free;
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; 
  X, Y: Integer);
begin
  //assign an independant node's values:
  FLastMousePos.x := X+Random(5)-2;
  FLastMousePos.y := Y+Random(5)-2;

  //tell the dataflow graph that we just assigned to an INDEPENDANT node's
  //values; it will update the dependant values for us:
  FNodes.Items[FNodeNames.NodeID('FLastMousePos')].HandleSet;
end;

procedure TForm1.SpinEdit1Change(Sender: TObject);
begin
  FNodes.Items[FNodeNames.NodeID('SpinEdit1.Value')].HandleSet;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  FFlag := not FFlag;
  FNodes.Items[FNodeNames.NodeID('FFlag')].HandleSet;
end;

end.

Conclusion

Well, that's today's programming recipe! Hope you've gotten something useful out of this! Below is the source code for both the library and the example:

Library Source
Example Source

Thursday, November 12, 2009

Power Tip: 'With'-Helpers


In the previous article, I mentioned that Delphi includes a 'with' statement, and although it seems like an unimportant for a language to have, I'm going to teach you something that will make you stand back and give you an alternate perspective on this simple mechanism. Sorry all you C++ fans, this power tip applies only to languages that have a 'with' statement.

With statements are the most useful for one thing: 'with'-helpers.

What is a 'with'-helper?

Yeah, I knew you were thinking that: a 'with'-helper is a class or data structure that is designed to be more convieniently used via a 'with' statement in Delphi. It's one of the most Ruby-like features that Delphi has to offer, brining a greater increase of convenience and fun to Delphi programming. The most quick way to demonstrate is through examples, so come on, let's take a look...

Examples

Let's start with a simple example: a work-around for variables. In Dephi, one normally has to explicitly declare variables before the block of code that uses them begins, like so:
procedure ReportMyError(Code: integer);
var
  //we're declaring the variable here in the "var" block
  vMsg: string;
begin
  if Code > 0 then
    if GetErrorMsgStr(Code, vMsg) then
      PrintLn('Error!'#13#10 + vMsg);
end;

This can quickly become inconvenient, so, why don't we create a 'with'-helper that provides a temporary string variable:
type
  TStringValue = record
    Value: string;
  end;

function StringValue: TStringValue;
begin
  Result.Value := '';
end;

And then use it like so:
procedure ReportMyError(Code: integer);
begin
  if Code > 0 then
    //calling the StringValue() function to create a new string value,
    //which is put into the context with the "with" statement:
    with StringValue do
      if GetErrorMsgStr(Code, Value) then
        PrintLn('Error!'#13#10 + Value);
end;

Did you notice where identifier called 'Value' came from? 'Value' was implicitly referenced from the TStringValue record returned by the StringValue() call and implicitly put in scope by the 'with' statement. Get all that? Thanks to Delphi's built-in support for records, we don't do any memory management! Yay! Delphi is better!

We can do much better than this, however. Let's create a nice 'with'-helper for providing the effect of a 'let' statement: create and compute a value, sort of like a closure:
type
  TFloatValue = record
    Value: extended;
  end;

function FloatValue(AValue: extended): TFloatValue;
begin
  Result.Value := AValue;
end;

Now we use it:
function EmitRatios(A, B: extended);
begin
  with FloatValue(Sqrt(Sqr(A)+Sqr(B))) do
    begin
      PrintLn(FloatToStr(Value/A));
      PrintLn(FloatToStr(Value/B));
    end;
end;

Sweet! The value passed to FloatValue() was only computed once! Yay for the extra efficiency boost! Records can do some good and come in handy-dandy using them. Along with what we've seen, we can also return multiple results from functions, capture them with a 'with' statement, and leverage them thus. But there's one problem with this last example: records can only do so much: their fields are mutable and cannot be protected from writing to them. This could cause horrible bugs in a program if we're not careful. Thus, we move on up to using classes as 'with'-helpers. Here's our previous example modified:
type
  TFloatValue = class
  private
    FValue: extended;
  public
    property Value: extended read GetValue;
  end;

function FloatValue(AValue: extended): TFloatValue;
begin
  Result := TFloatValue.Create;
  Result.FValue := AValue;
end;

//using it:

function EmitRatios(A, B: extended);
begin
  with FloatValue(Sqrt(Sqr(A)+Sqr(B))) do
    try
      PrintLn(FloatToStr(Value/A));
      PrintLn(FloatToStr(Value/B));
    finally
      Free;
    end;
end;

Notice the try-finally block for freeing the created class called by and provided for the 'with' statement. Delphi knows that the structure used by the 'with' statement should have its members in the enclosing scope, so this technique will also work even if the 'with' statement is already inside a class method.

Now that we're using classes, we can do more sophisticated stuff, seeing as classes provide not only properties, but methods. As a last example:
type
  TTokens = array of TToken;

procedure Append(AToken: TToken; var Tokens: TTokens);
begin
  SetLength(Tokens, Succ(Length(Tokens)));
  Tokens[High(Tokens)] := AToken;
end;

function ScanTokens(AStr: string): TTokens;
begin
  SetLength(Result, 0);
  //constructs a TScanner class, giving it the 
  //string to scan: TScanner = token iterator
  with Scanner(AStr) do
    try
      while ReadNextToken do //TScanner.ReadNextToken method
        Append(CurToken, Result); //TScanner.CurToken property
    finally
      Free;
    end;
end;

Conclusion

Thanks for learning. Hope you enjoyed this power tip. Happy coding!

Tuesday, November 3, 2009

Be Advised: C++ vs Delphi

While thinking about what knowledge would be of most use to the rest of programmerdom, I realized that an objective, non-biased, experienced, comparison between C++ and Delphi would provide valuable information to one and all, that all may be more educated as to the tradeoffs of these languages, their pros and cons, and perhaps be able to glean valuable insight to pass on to the next generation of programming languages.

I hear that everybody loves flamewars. If you’ve come for that, please leave now.

Still here? Ok, nice. If you’re like me, you know that anyone who is wedded to programming language X, has not spent enough time in their lives programming nor studying programming languages, to have serious experience with or understanding of programming languages. However, if you are one who has come for an experienced comparison of C++ and Delphi, well, you will not have wasted your time (inasmuch as I’m not restating what you already know). Let me just give 1 caveat: I have much more experience with Delphi than with C++, so I may have left a couple of advantageous features of C++ out. If I do so, please remind me, and I will be sure to include such (provided they meet the criteria of being exclusive, marked advantages.) Now, enough with the fluff, let’s get down to the meat. The following lists features that vary from Delphi to C++, listing the winning language:

FeatureWinnerExplanation
ClosuresDelphiC++ just doesn't have closures. Closures are an advantageous way to organize long procedures of code into smaller, more reusable pieces.
Operator OverloadingC++Delphi doesn't have this feature. Operator overloading is useful for terse arithmetic libraries over different kinds of quantities, such as intervals, bignums, and others, instead of having to invoke a bunch of method calls by name. In addition, expressions can be semantically overloaded and more reusable via OOP.
InterfacesDelphiC++ doesn't have interfaces. Interfaces are like mixins without implementations: they provide a description of a set of methods that a class would need to implement to be compatible with the interface. Subroutines can use interfaces to be more generic and reusable, by accepting, as a parameter, any object that implements a specified interface. Classes in Delphi can implement as many interfaces as they like. This provides a simple alternative to multiple inheritance.
Multiple InheritanceC++Delphi just doesn't have multiple inheritance. Multiple inheritance is where an object can inherit fields and methods from more than one base class. In practice, this is very complicated and is usually overkill, but for those situations in which it makes sense, C++ provides this alternative.
PropertiesDelphiIn C++, one must use getters and setters to write to the properties of a class. In Delphi, one describes properties, and can direct reading and writing to either a field or a getter and setter method, or describe whether a property can even be read from or written to. In addition, one can reference a property via the dot notation. e.g. "GoButton.Enabled" and use such an expression for reading and writing, without having to directly invoke getters and setters. This provides better abstraction.
Module SystemDelphiIn C++, you have header files and C++ files. The header file declares what code is available to other C++ files. The preprocessor is assigned with the task of providing each C++ and header file with knowledge of its dependencies. In Delphi, a module is a single file called a unit. The unit lists what code is public to other units (called the interface of the unit), as well as housing the implementation of the public declarations. Units lists their dependency on other units via the "uses" clause. In addition, Delphi provides optional initialization and finalization sections, so that the unit can initialize or free any internal state it uses. These features all provide for a much more robust seamless module system, giving each unit airtight encapulation facility. Of course, Delphi is a member of the Pascal family of languages, which are all known for their robust module systems.
Constructors and Stack ObjectsC++C++ has a standard system for copying objects, in addition to the normal constructor used for creating objects. This streamlines this utility. In addition, C++ then provides a way to declare object variables that are automatically created and managed on the stack. This provides better abstraction for temporary helper objects, which can then be treated as data types. In Delphi, however, all objects are created on the stack, and must be explicitly created and freed. Object variables are actually assignable, nullable references to object instances. Also, Delphi does not have a built-in system for copying objects or assigning their values around, although there is a standard method convention used for assigning objects to each other.
Templates and Generic ClassesC++Delphi does not have templates, nor does it have generic classes. This means that algorithms and data structures must be implemented differently for objects and primitive types. C++ provides templates, which allow any piece of code to be reused for different, but compatible, types, primitive or not.
Method PointersDelphiDelphi provides well-developed facilities for managing pointers to methods and subroutines. This comes in handy for event-handling code and other related uses. C++ on the other hand, is very messy, requiring one to get into some hairy types. Generally, using pointers to methods or subroutines, is not recommended. Instead, C++ relies on templates to facilitate the coupling of objects to event handlers.
Error MessagesDelphiC++'s error messages are often cryptic and do not provide intuitive or simple feedback to the programmer. Delphi always provides simple, direct error messages (although both languages use programming-centric terminology).
Encapsulated Class DefinitionsC++C++ allows one to declare a class inside a class. This is useful in some cases, so C++ provides the option. As far as I know, Delphi does not.
HackerinessC++C++ is more all-around hacker-frendly, and relies more on the use of symbols and operators, making it, in comparison to natural language, more cryptic. Delphi uses alot more english words and alot less symbols, for its code. Of course, experience with either, will render this difference negligible, but hackers tend to enjoy symbols more.
ReferencesC++C++ has references as a type. References are a more abstract version of pointers, and have lean implementations. Thus, they can come in handy for simplifying code and making it more readable. Delphi, while allowing one to pass parameters around by reference, does not have a reference type -- just pointers.
Dynamic ArraysDelphiDelphi has built-in, leanly-implemented, runtime-resizeable arrays of any dimension. These can come in handy in many situations, since Delphi lacks generics. C++ has no such thing, instead relying on its generics to achieve the same functionality.
Strings and String ManagementDelphiDelphi has built-in, leanly-implemented, memory-managed strings. Thus, using strings in Delphi is a breeze. The same is not so true for C++, but it covers most all of the ground, in ease-of-use, with its String class.
Compilation TimeDelphiDelphi was designed to compile fast, and to only compile the units that have changed since their last compile. Thanks to its module system, this is more easily achievable. In practice, this means that the compile-run cycle is quick, since the compiler will only compile the units you make changes to.
In-block VariablesC++C++ allows one to declare variables anywhere inside a block of code. This is much more convenient for writing code than in Delphi, which requires all variables to be declared in the declarations section, preceding the implementation blocks of code. Although one might argue that there's a trade-off between more readable code and more writable code. But I'd prefer C++'s choice on this one.
VariantsDelphiDelphi provides a primitive type called a Variant, which is simply a data type that can only any other primitive type (no compound types, pointers, objects, or dynamic arrays). This can be useful in situations where one would otherwise have to write lots of type-handling code. This type as evolved into the OLEVariant type, which allows Delphi programmers a choice when coding for COM and OLE automation, between early-bound and late-bound style.
SetsDelphiDelphi's equivalent to C++'s enumeration type, is much more well-abstracted. Delphi provides set union and difference operators.
Open Array ParametersDelphiIn addition to dynamic arrays, Dephi provides a way to pass arrays as parameters. It even provides a notation for declaring array-literals as parameters. e.g. "PrintLn(["hi",a,"two",x])" This is extremely handy when developing and using DSL-like libraries, such as parsers. C++ doesn't quite have this functionality.
"with" statementDelphiC++ does not have "with" statements. "With" statements are very useful for unraveling repeated object references and putting the referenced object's properties and methods automatically in the local scope. This seems like a trivial convenience, but we'll see later on why it's such an important ingredient to the repertoire of delphi's constructs.

Conclusion

So, after looking over this list of features, there's a couple of patterns that stand out -- consistent language design choices made. Delphi is intended to be an applications programming language, seeking to provide for more abstraction in many common areas -- strings, modules, open array parameters, late-bound COM programming. This is a common thread running through the design decisions of Delphi. Don't get me wrong, it's also intended to create programs of competitive efficiency with C++, but the point is that Delphi is focused on providing more abstraction for applications programmers. C++ is a systems programming language, designed to not trade a single bit of efficiency away for abstraction. It tries to do what it can for abstraction, but the guiding principle, indeed, the constraint, upon C++, is that there is nothing in the language that prevents the programmer from hand-optimizing efficiency. Language design choices are a different subject altogether, though. Just remember that language differences don't necessarily imply general superiority or inferiority. C++ is a better systems language, and Delphi is a better applications language. Each has its own optimal role.

Introduction

Hi!

Welcome to the overcoded! blog. My name is Winheim Raulsh. I am the writer of this blog and will be your guide, that you may better navigate this trove of information I periodically gather together and spill onto pages. I am a Delphi programmer by birth, and have come to know many languages like C++, Lua, and others, but mainly I will stick to my native language: Delphi.

Delphi is my default choice, as I have been programming in it for years, and it has served me well. Delphi is a systems and desktop programming language available for developing on the Win32 platform, hence it is very fast (comparable to C++) so that programs can scale without having to be rigorously optimized, yet it is a more high-level language. Delphi, ah, Delphi. It has its limitations, and isn’t anything particularly spectacular, but it can do many good things. I’ve learned most everything I ever would need to know about Delphi, and then some — all this knowledge wrapped up in my head, with nothing to do with it, so I started this blog for really no reason, except simply to kill time and do fun things with Delphi, just because I can. That’s where the name for this blog comes from: excess pointless Delphi coding for no great, grand reason — my life is overcoded.

Sometimes, I wish I could program in Lua instead. Lua is different; it doesn’t scale; you can’t write 20k LOC programs in it, you run very complex or tight computations in it, without the speed bogging down; nor is it a systems programming language with access to all of the Win32 api, nor does it come with all the useful, pre-developed gui libraries, database access, and other stuff that comes with Delphi. It’s only a scripting language, all by itself, so I’m stuck with Dephi. And that, ladies and gentlemen, is a classic case in a high barrier-to-entry, in the world of software development.

Now, where was I? Ah, yes: let me give a grand tour of how we do things around here. As I was thinking about what kinds of articles to do, I liked the idea of having different series of ongoing articles, so pretty much all articles are a part of one series or another. How did I come tho that? Basically, I narrowed down the basic purposes of different articles I would write to only a handful. It really helps one to quickly determine what the articles are throwing at you. As a blogger, I will stick close to the purpose and subject of my entries, so don’t expect much philosophical discussion, unless you’re forewarned with a title or section header, ’cause I’ll be keepin’ it on-topic.

Series

The following is a tour-guide of the different types of entries I’ll write.

Be Advised

These articles contain relatively refined knowledge and experience of practical things pertaining to software development. If I have been there, studied the issues, and have a well-developed knowledge of the situation, then consider these articles as troves of useful information to take into consideration while making decisions, or for future reference.

Power Tip

These articles contain various useful coding tricks and practical techniques for improving your development experience. They are very narrow, focused, specific things you can do. These tips are straightforward and easy to wrap your head around; they don’t require pondering to understand.

Goodies

These articles introduce free code libraries & other such stuff that I provide online! They will document such things as why, basic usage, and a download link. Feel free to use the code according to the license embedded within its downloaded version, otherwise it implicitly falls under the Create Commons Attribution-ShareAlike 3.0 License.

Queries

These articles are an opportunity for me to ask you, the reader, to enlighten me with valuable insight or information on something I can’t quite seem to grok. Explain away! Comments welcome! (Not that they’re unwelcome in other situations.) BTW, thanks in advance!

Reflections

Obviously, if I have something I feel is very important to say — something I’ve reflected upon, probably theoretical or philosophical, that helps me understand or achieve insight into the vastly complex world of programming or programming languages — I will put it in this category. Feel free to express your thoughts (on-topic, of course). I guess I would really only expect two types of responses: affirmations & critiques. ‘Course, you can criticize the ideas all you like (I don’t mind). If you find the ideas useful, please comment on how exactly so.

Programming Recipes

Unlike Power Tips, these entries get deep into the nitty-gritty of larger code solutions to various problems — recipes to code. Perhaps some of the problems can be requested by you. First, I’ll start off with an explanation of the problem, followed by an overview of the solution, followed by a laid-out, detailed walkthrough of the coding. Think of the programming version of a chef show on TV, and you have a good idea of what these articles are for. Please feel free to evaluate my solutions and suggest corrections! All your constructive feedback will especially help!

Enlightenment

Enlightenment is my explanations of concepts when I finally grok them, that is, not just what they are, but why they are, where they come from, and what they do for us. These are the kinds of things I wished computer scientists and instruction materials (all on the internet) had explained to me in terms that are familiar and clear, so feel free to learn new things, and hopefully, you can find enlightenment as well!

Conclusion

So, that’s the tour! Thanks for stopping in, now, enjoy the journey as your life becomes overcoded!