Fluent coding - debugging

That’s an interesting thread and an interesing point by the questioner about REST services returning 404 as a regular flow state rather than an error. That obviously wouldn’t have been the case for HTTP servers that were serving only HTML when Indy was first written.

The point of that part of the exercise, for me, was to understand how expensive exceptions are

I was pretty sure that was what you were doing.

Also, just saying, if you google “exceptions are bad and should be avoided” … there’s plenty of discussion.

I’ve just been involved in porting some C code to Delphi. C has no exceptions so that code had lots of checking of boolean return values, and lots of special case return values e.g. -1 indicating an error.

After we completed the line by line exact translation to Delphi, I went back and Delphi-fied the code. One of the things I did was to replace most of those boolean and special case return values with exceptions.

It simplified the code considerably. Being able to remove all those return value checks reduced the line count of some of those calling functions substantially and as a result they are much more readable now.

There were just a few instances in the translated Delphi code where I left the return values in. From memory they were in sections where the original code was taking a trial and error approach within a tight loop. If I’d converted those functions to use exceptions I would have seen the kinds of performance issues you’ve been documenting.

Within the rest of the translated C to Delphi code almost all the checks of the return values basically emulated the behaviour of exceptions anyway. A function returned a failure, the calling function detects that failure and then itself returned a failure, and so on right back to the original caller.

It’s pretty common to see that sort of return value based error checking code follow that “exception lite” pattern, of just giving up and returning all the way back to the original caller. In my opinion that’s because writing meaningful error recovery code is in most cases just too damn hard.

Return values can make sense at the micro level but at the macro level exceptions are usually my tool of choice.

1 Like

There’s pattern that I have used in c# - not an ‘official’ pattern but I have seen it call the OperationResult pattern. It maps really well to web applications because there’s kinda a finite number of results you can send back, ie success, bad request, forbidden etc

This is how it might look in delphi (untested and perhaps contrived)

unit VSoft.OperationResult;

interface

uses
  Generics.Defaults;

type
  TOperationResultKind = (
    Success,
    BadRequest,
    Forbidden,
    NotFound,
    Error);

type
  TOperationResult<T> = record  //using a record to simplify memory management.
  private
    FKind     : TOperationResultKind;
    FMessage  : string;
    FValue    : T;

    constructor Create(const kind : TOperationResultKind; const message : string; const value : T);
    function GetSuccessful: boolean;
    function GetFailure: boolean;
    function GetNotFound: boolean;
    function GetError : boolean;
    function GetForbidden : boolean;
    function GetBadRequest : boolean;
  public
    class function CreateSuccess(const value : T) : TOperationResult<T> ;static;
    class function CreateError(const message : string = '') : TOperationResult<T> ;static;
    class function CreateNotFound(const message : string = '') : TOperationResult<T> ;static;
    class function CreateForbidden(const message : string = '') : TOperationResult<T> ;static;
    class function CreateBadRequest(const message : string = '') : TOperationResult<T> ;static;
    
    function ToRestResult : TRestResult;

    property Kind : TOperationResultKind read FKind;
    property Message : string read FMessage;
    property Value : T read FValue;

    property Successful : boolean read GetSuccessful;
    property Failure : boolean read GetFailure;
    property NotFound : boolean read GetNotFound;
    property Forbidden : boolean read GetForbidden;
    property BadRequest: boolean read GetBadRequest;
    property Error : boolean read GetError;
  end;
....

To use it, say you have a function

function GetCustomerById(const customerId : integer) : TCustomer;

It either returns a TCustomer instance, or null, or it could raise an exception. That means you need exception handling. We could change it so it used the operationresult pattern and never raises exeptions and never returns null

function GetCustomerById(const customerId : integer) : TOperationResult<TCustomer>;

To call it you might have have

function TRestController.GetCustomer(const id : integer) : TRestResponse //contrived api
var
  opResult : TOperationResult<TCustomer>
begin
  opResult := GetCustomerById(id); //never returns null
  result := opResult.ToRestResult; //helper method that maps operationresult to whatever we return in our rest controller
end;

in GetCustomerById we might have something like

function GetCustomerById(const customerId : integer) : TOperationResult<TCustomer>;
var
  customer : TCustomer;
begin
  try
    CheckAuthorised; //throws if the current user does not have permission;
    ValidateId(customerId); //throws if negative 
    customer := GetCustomerFromDB(customerId);
    if customer <> nil then
      result := TOperationResult<TCustomer>.CreateSuccess(customer)
    else
      result := TOperationResult<TCustomer>.CreateNotFound('customer not found'); 

  except
     on e : UnauthorisedException
     begin
      result := TOperationResult<TCustomer>.CreateForbidden(e.Message);
     end;
     on e : EArgumentOutofRangeException
     begin
      result := TOperationResult<TCustomer>.CreateBadRequest(e.Message);
     end;   
     on e : Exception
     begin
      result := TOperationResult<TCustomer>.CreateError(e.Message);
     end;
  end;
end;

This could be tidied up further by adding a CreateFromException method to TOperationResult that understands the possible exceptions and creates the appropriate result type.

In c# I use extension methods to take it further, so I have one that converts the operationresult to an IActionResult (asp.net controller result).

The idea here is to

a) never return null
b) never let exceptions bubble up
c) make error handling simpler.

Note : Above code typed in discourse so might not be valid!

1 Like

I must admit I’m not really seeing the true utility of your TOperationResult<T>. Right now it looks to be a mostly a more typesafe version of sort of C code we just ported to Delphi which we also switched from using return values to raising exceptions.

Now I’d rather be returning your TOperationResult<T> than a value -1 to indicate failure like that C code mostly did but I’m not convinced it’s a better approach than raising exceptions directly at the point of failure.

Adapting your example I would have something like

type
  EFailure = class(Exception);
  ENotFound = class(Exception);
  EForbidden = class(Exception);
  EBadRequest = class(Exception);
  EError = class(Exception);

with the success state indicated by completing the flow without an exception.

function TRestController.GetCustomer(const id : integer) : TRestResponse;
var
  customer : TCustomer
begin
  try
    customer := GetCustomerById(id); // raises an ENotFound rather than return nil
    try
      Result := TRestResponse.Create(customer);  // builds JSON from the customer
    finally
      customer.Free;
    end;
  except
    on E: Exception do
      Result := TRestResponse.Create(E);
  end;
end;

And the code that does the actual work, returns a customer and throws exceptions for any sort of error (re-raising specific errors as our custom exception types above).

function GetCustomerById(const customerId : integer) : TCustomer;
begin
  try
    CheckAuthorised; //throws if the current user does not have permission;
    ValidateId(customerId); //throws if negative 
    customer := GetCustomerFromDB(customerId);
    if customer = nil then
      raise ENotFound.Create('No customer with ID %d', [customerid]);

  except
     on e : UnauthorisedException
       raise Exception.RaiseOuterException(EForbidden.Create(e.Message));
     on e : EArgumentOutofRangeException
       raise Exception.RaiseOuterException(EBadRequest.Create(e.Message));
  end;
end;

The TRestResponse class would have number of constructors, such as a constructor to convert an object to JSON for successful requests and for unsuccessful requests a constructor that converts an exception to an HTTP response.

constructor TRestResponse.Create(E : Exception);
begin
  FResponseText := E.Message;
  if E is EFailure then
    FHttpStatus := 500
  else if E is ENotFound then
    FHttpStatus := 404
  else if E is EForbidden then
    FHttpStatus := 403
  else if E is EBadRequest then
    FHttpStatus := 400
  else
    FHttpStatus := 500;
end;

Perhaps as you suggested this is too contrived an example to show the true worth of the pattern, but I’m not that bothered about the first 2 of your goals, and I’m pretty happy with the simplicity of my approach.

I hate seeing the same boiler plate error handling code repeated over and over.

I’m not a fan of raising exceptions for errors, in particular for the case where the item was not found (not saying I never do it though).

In my real code (c#) I use an extension method to convert the operationresult to a rest response… so the operationresult is not tied to the rest response type.

I guess there are different ways to skin a cat :wink:

The operation of a monad is to be able to compose functions that are not on the face of it composable.
So eg the Writer monad would be a composition rule like:

function 1 ( input )  ->  [ output1, string1 ]  ;
function 2 ( input )  ->  [ output2, string2 ]  ;

function 1 . function 2 "=" function2 ( function1 (input1) )
                         = [ function2 ( output1 ), string1 + string2 ]

ie: “function composition” can mean successively composing the functions, and concatenating the strings.
And the operation is called “Bind”.

This link shows OnSuccess() as a composition rule / bind operation in C#: Functional C#: Handling failures, input errors · Enterprise Craftsmanship

[HttpPost]
public HttpResponseMessage CreateCustomer(string name, string billingInfo)
{
    Result<BillingInfo> billingInfoResult = BillingInfo.Create(billingInfo);
    Result<CustomerName> customerNameResult = CustomerName.Create(name);
 
    return Result.Combine(billingInfoResult, customerNameResult)
        .OnSuccess(()  => _paymentGateway.ChargeCommission(billingInfoResult.Value))
        .OnSuccess(()  =>  new Customer(customerNameResult.Value))
        .OnSuccess(
                 customer  => _repository.Save(customer)
            .OnFailure(()  => _paymentGateway.RollbackLastTransaction())
        )
        .OnSuccess(()  => _emailSender.SendGreetings(customerNameResult.Value))
        .OnBoth(result =>  Log(result))
        .OnBoth(result =>  CreateResponseMessage(result));
}

(I like their super concise lambda expressions, but I’m not so keen on the use here)

Bringing things around in a circle … I had totally forgotten Nick Hodges’ talk on aspect-oriented programming (2014) was about VirtualMethodIntercept. And I had definitely not taken onboard that it was really talking about DSharp letting you use attributes to make the code nicer.

So maybe there might be a fairly nice way, in that direction, to express composition rules like this OnSuccess() that can cope with successes and failures.

Hi!

Can I speak heresies in here? I will go ahead and say that, really, you’re not quite supposed to debug fluent APIs of any kind in my view.
What you should definitely be doing is:

  • Unit test them
  • Make sure each method’s implementation is “short and sweet”, i.e. the usual 25-50 lines of code
    excluded comments, yadda yadda yadda you know

I find that the vast majority of the code employing both things rarely needs debugging. And whenever an edge case arises, you can generally tell quite easily where it’s failing and develop a test for it, so that you know it’s fixed.
If you find that you’re debugging Fluent APIs then that’s generally either out of a lack of unit testing or the apis are too granular or they are too coarse or even a mix of all the former!
Fluent APIs are fundamentally very difficult to implement and very easy to design.

The principles are quite simple and easy to grasp but the implementation is incredibly difficult.

Note that while these general principles should apply to all code, fluent code is particularly painful to deal with without these.

I have an example of Fluent Coding (I Think) from a Delphi Sample

Fully united tested.
Worked last week.
Minimal changes to inputs.

Now Exceptions out

Not a fan of Fluent.

Result := TJNotificationCompat_Builder.JavaClass.init(TAndroidHelper.Context, StringToJString(NotificationChannelId))
    .addAction(TAndroidHelper.Context.getApplicationInfo.icon, StrToJCharSequence('Stop location tracking'), GetServicePendingIntent)
    .setPriority(TJNotification.JavaClass.PRIORITY_HIGH)
    .setOngoing(True)
    .setSmallIcon(GetNotificationIconId)
    .setContentIntent(GetActivityPendingIntent)
    .setContentTitle(StrToJCharSequence(NotificationTitle))
    .setContentText(StrToJCharSequence(NotificationContent))
    .setTicker(StrToJCharSequence(NotificationContent))
    .setWhen(TJDate.Create.getTime)
    .build;

I think you could still use the Identity construction to break up the process
… but could the actual problem be related to something about a deprecation ?

The core issue with fluent API in delphi is the debugger. Since the result values are implicit meaning there is no variable that you could inspect to check the value.
Visual Studio (at least for C#) for example always shows the last result returned from a function even if you don’t explicitly assign it to a variable making it very convenient. I also think that you can place breakpoints better there - in Delphi often the blue dots go all over the place making it hard to put a breakpoint in the middle of the fluent API chain. And it also highlights the code way better when stepping through showing you exactly where you are in some complex statement.

It’s often an issue with the delphi debugger. I posted this privately to embarcadero a while back, the response from embarcadero was… crickets…


Delphi’s (and C++ builders from what I read) debuggers are woefully poor at the one job they need to do - debugging.

When I launch my app under the debugger (Win32 in this case, the other platforms are even worse), I never know for sure if it will work, or if the IDE/debugger will hang, or if the IDE will just get an external exception and crash the IDE.

Today it was the latter, and only a reboot would solve the external exception issue. That was on the first run of the day, with a freshly started IDE, not a good start to the day!

And then there is the case where stepping suddenly doesn’t … step that is, I have to stop the session and try again… but that often then results in internal compiler errors and I have to restart the IDE.

If this was the norm in dev tools I wouldn’t be posting here, but it isn’t. I’ve been doing a lot of work in VS2019 over the last few weeks, working on an asp.net core web application and the experience couldn’t be more differert.

The debugger in VS has been flawless. Over the last week or so I’ve been debugging with it running under linux/WSL2 and it starts up, runs, steps etc just as snappy as when debugging it running on windows. I just had to install the sdk on wsl (I guess that probably contains a debugger, no idea) but however they do it, it just works, perfectly, every time.

Fixing the debuggers (and I mean, really fixing them) would remove 50% of the negativity around Delphi - of which there is plenty.

I don’t pretend to know what that would take, or the technicalities - my limited experience of writing a debugger (I wrote a code coverage tool, it was buggy) tells me it’s not easy. But then if it was easy, everyone would be doing it.

My 0.02c.

This is why I was investigating this idea of injecting an identity step into the chain, as a debug landing point, with the ability to skip over steps that don’t need examination.

function TTestIntfImp.Id : ITestIntf;
begin
  Result := Self;
end;

begin
    TTestIntfImp.MakeIntf
                .FluentSetName('My name is bob').FluentSetAge(8)
                .FluentSetName('My name is tom').FluentSetAge(9)
                .id
                .FluentSetName('My name is peg').FluentSetAge(3)
                .id;
end.

Just looking at fluent calling for …

  • record returning self, (creates multiple copies progressively)
  • record returning self pointers, and
  • class returning self

32 bit code reads a bit neater than 64 bit.

Returning value types on which you continue to perform mutating operations is a bad idea to begin with regardless the codegen.
That leaves pointer to record or object (which is basically a pointer). Both the same generated code. Now we can add interfaces to the mix but I can tell you, you will see a ton of interface-specific calls there. Making those returns [unsafe] would fix that but iirc that feature was (or still is?) broken in some cases.

1 Like

I haven’t heard about [unsafe] before your comment … :thinking:

https://blog.marcocantu.com/blog/2016-april-weak-unsafe-interface-references.html