DIVEX

Bubbling up of parameters

One thing DIVEX is about is the bubbling up of parameters. It is the ability for developers to modify low-level classes or functions to add new parameters, and then have these parameters automatically be parameters of higher-level functions that use the low-level functions/classes. Consider this example:

[DivexCompose]
public static partial class Program
{
    public record Document(string Content, string Name);

    static void Main(string[] args)
    {
        DelFunc.DF1 getDocumentOwnerEmail = (Document document) =>
        {
            return document.Name + "@test.lab";
        };

        DelFunc.DF2 processDocument = (Document document,
            Func<Document,string> getDocumentOwnerEmail,
            Action<string /*to*/, string /*body*/> sendEmail) =>
        {
            Console.WriteLine(document.Content);

            var ownerEmail = getDocumentOwnerEmail(document);

            sendEmail(ownerEmail, "Your document has been processed");
        };

        var sendEmailAndLog = SendEmailAndLog();

        var process = processDocument
            .Inject(sendEmail: sendEmailAndLog)
            .Inject(getDocumentOwnerEmail: getDocumentOwnerEmail);

        process.Invoke(document: new Document("Hello", "Name"));
    }

    public static VarReturn.VR1 SendEmailAndLog()
    {
        DelFunc.DF3 log = (string message) =>
        {
            Console.WriteLine(message);
        };

        DelFunc.DF4 sendEmail = (string to, string body, Action<string /*message*/> log) =>
        {
            Console.WriteLine("Sending email to " + to);
            Console.WriteLine(body);

            log("Sending email");
        };

        var sendEmailAndLog = sendEmail.Inject(log: log);

        return sendEmailAndLog;
    }
}
DelFunc.DF1, DelFunc.DF2, DelFunc.DF3, and DelFunc.DF4 areĀ Delegate Functions. Delegate Functions is a way to support treating lambdas as functions.

In this example, four basic functions are defined:

  • log (inside the SendEmailAndLog method)
  • sendEmail (inside the SendEmailAndLog method)
  • getDocumentOwnerEmail (in the Main method)
  • processDocument (in the Main method)

The following diagram shows the the composition of these functions:

The processDocument is the root function, it directly calls getDocumentOwnerEmail and sendEmailAndLog. sendEmailAndLog is the result of injecting log into sendEmail.

We say that processDocument is the highest-level function. The sendEmail function’s level is lower. The the log function’s level is lower than that of sendEmail.

Now, the nice thing about DIVEX is that if we simply change the signature of the log function to add a DateTime parameter (for example) and save, this new DateTime parameter will bubble up all the way to the process function. If you inspect the signature of the sendEmailAndLog and process functions (in the Main method), they will both have a new DateTime parameter. Now, if we are two invoke the process function, we must pass a value for this new DateTime parameter. Such value will be used as the value of log’s DateTime parameter whenever it is called.

[DivexCompose]
public static partial class Program
{
    public record Document(string Content, string Name);

    static void Main(string[] args)
    {
        DelFunc.DF3 getDocumentOwnerEmail = (Document document) =>
        {
            return document.Name + "@test.lab";
        };

        DelFunc.DF4 processDocument = (Document document,
            Func<Document,string> getDocumentOwnerEmail,
            Action<string /*to*/, string /*body*/> sendEmail) =>
        {
            Console.WriteLine(document.Content);

            var ownerEmail = getDocumentOwnerEmail(document);

            sendEmail(ownerEmail, "Your document has been processed");
        };

        var sendEmailAndLog = SendEmailAndLog();

        var process = processDocument
            .Inject(sendEmail: sendEmailAndLog)
            .Inject(getDocumentOwnerEmail: getDocumentOwnerEmail);

        process.Invoke(document: new Document("Hello", "Name"), time: DateTime.Now);
    }

    public static VarReturn.VR1 SendEmailAndLog()
    {
        DelFunc.DF1 log = (string message, DateTime time) =>
        {
            Console.WriteLine(message);
        };

        DelFunc.DF2 sendEmail = (string to, string body, Action<string /*message*/> log) =>
        {
            Console.WriteLine("Sending email to " + to);
            Console.WriteLine(body);

            log("Sending email");
        };

        var sendEmailAndLog = sendEmail.Inject(log: log);

        return sendEmailAndLog;
    }
}