DIVEX

Composition Language 1

Because DIVEX make composition code verbose, I decided to create a new language that makes it easier to work with DIVEX. I call this language Composition Language 1. This language is based on C#. Actually the new language compiles to C#.

You can try this language in the browser here: https://divex.dev/try-in-the-browser-cscompose/

Inside Visual Studio (with the DIVEX extension of version 0.9.38 installed), you can try this language by creating a file with the extension of cscompose in a C# project and setting its Build Action to AdditionalFiles like this:

Note: if you don’t find AdditionalFiles as a value for Build Action, select “C# analyzer additional file”.

See Getting Started for guidance on how to install and use DIVEX inside Visual Studio. That guide does not talk about Composition Language 1. If you follow that guide, and wish to use Composition Language 1, you can simply rename Program.cs to Program.cscompose and set it’s build action to AdditionalFiles like described above.

The language has several features.

Easier usage of lambdas

using System;
using DIVEX.Core;
[DivexCompose]
public static partial class Program
{
    public static void Main()
    {
        var func1 = (int a, int b) => a + b;
        Console.WriteLine(func1(1,2));
    }
}

In the example above, I didn’t have to use the verbose DelFunc.DF1, etc, to define a lambda.

Ability to invoke functions without the verbose Invoke method call

In the example above, I was able to invoke the func1 functions immediately without using the Invoke method.

Less verbose syntax for Apply, Replace, and Inject

using System;
using DIVEX.Core;
[DivexCompose]
public static partial class Program
{
    public static void Main()
    {
        var func1 = (int a, int b) => a + b;
        var func2 = func1[a: 1];
        Console.WriteLine(func2(2));
    }
}

In the example above, I partially applied the func1 function using square brackets. This is less verbose than using the Apply operator.

using System;
using DIVEX.Core;
[DivexCompose]
public static partial class Program
{
    public static void Main()
    {
        var func1 = (int a, int b) => a + b;
        var func2 = func1[a: (string str) => str.Length];
        Console.WriteLine(func2(b: 2, str: "test12"));
    }
}

The above example demonstrates how the square brackets syntax can be used to mimic a usage of the Replace operator. It also demonstrates how easy it is to use lambdas in this composition language. Parameter a of func1 was replaced by a new function presented as a lambda. Now func2 has the original b parameter of func1, and a new str parameter.

using System;
using DIVEX.Core;
[DivexCompose]
public static partial class Program
{
    public static void Main()
    {
        var func1 = (int a, Func<int, int> f) => a + f(5);
        var func2 = func1[f: (int i, string str) => str.Length + i];
        Console.WriteLine(func2(a: 1, str: "test12"));
    }
}

The example above demonstrate a usage of the square bracket syntax that mimics the behavior of the Inject operator.

Simpler syntax for Rename

Renaming a parameter is now less verbose (compared to the Rename operator):

using System;
using DIVEX.Core;
[DivexCompose]
public static partial class Program
{
    public static void Main()
    {
        var func1 = (int a, int b) => a + b;
        var func2 = func1[b -> c][a: 1];
        Console.WriteLine(func2(c: 2));
    }
}

Again, using the square bracket syntax, I was able to rename the b parameter of func1 to c. Please note that you cannot mix rename operations with the other operations (Apply, Replace, and Inject) in the same square brackets.

Apply, Replace, and Inject by Index and by Type

When using the square brackets syntax to do Apply, Replace, or Inject operations, not only can you specify parameters by name, but you can also specify them by index or type.

using System;
using DIVEX.Core;
[DivexCompose]
public static partial class Program
{
    public static void Main()
    {
        var func1 = (int a, int b) => a + b;
        var func2 = func1[6];
        Console.WriteLine(func2(2));
    }
}

The code above partially applies the func1 function with the value 6. It will be parameter a that will have its value fixed to 6. This is because a is the first parameter. The same thing works with Replace and Inject.

using System;
using DIVEX.Core;
[DivexCompose]
public static partial class Program
{
    public static void Main()
    {
        var func1 = (int a, string b) => a.ToString() + b;
        var func2 = func1[+"str1"];
        Console.WriteLine(func2(2));
    }
}

The code above demonstrates specifying a parameter by type. Notice the + prefix used when supplying the “str1” argument. This means that the type of this value will be used to determine which parameter to use. In this case because we supplied a string, and b has type string, b’s value was fixed to “str1”.

If you are to mix different ways of specifying a parameter (by index, by type, and by name) in the same square bracket invocation syntax, you need to specify arguments by index first, then arguments by type, then arguments by name. The following is valid:

using System;
using DIVEX.Core;
using static DIVEX.Core.DivexUtils;
[DivexCompose]
public partial class Program
{
    public static void Main()
    {
        var add = (int a, int b, string c) => a.ToString() + b.Totring() + c;
        var add1 = add[1, +"str", b:3 ];
        Console.Write(add1());
    }
}