A custom Higher-Order Function node + BuildAssignment structure

Hello,

I’m looking for a way to create a zero-touch node that takes a function as an argument. Technically, functions taking other functions as arguments are called HOF (higher-order functions) and Dynamo got such the nodes, e.g. List.Map, Function.Apply or Function.Compose.

For clarity, let’s say I want to recreate Function.Apply taking a function as first argument and single inputs as the rest plus performing additional operations on the function results. My first approach was to use delegates as follows:

public class object MyApply(Delegate func, params object[] args)
{
    return func.DynamicInvoke(args);
}

But actually nope, this approach doesn’t work as dedicated Function type is required instead of Delegate. I looked into Dynamo sources and found that the original Function.Apply is built on DesignScript-based FunctionObject.ds library and its __ApplyList method.

The __ApplyList method is then triggered by Apply.cs:

public override IEnumerable<AssociativeNode> BuildOutputAst(List<AssociativeNode> inputAstNodes)
{
    return new[]
    {
        AstFactory.BuildAssignment(
            GetAstIdentifierForOutputIndex(0),
            new FunctionCallNode
            {
                Function = AstFactory.BuildIdentifier("__ApplyList"),
                FormalArguments = new List<AssociativeNode>
                {
                    inputAstNodes[0],
                    AstFactory.BuildExprList(inputAstNodes.Skip(1).ToList())
                }
            })
    };
}

My problem is: I would like to use __ApplyList output in further operations in my custom node. Is there any way to get numerical results out of FunctionCallNode or BuildAssignment before BuildOutputAst return? Also preferably without interfering DesignScript as external DLLs must be used on values returned by __ApplyList.

Maybe any other way to take function as argument, without BuildIdentifier("__ApplyList")?

1 Like

@Michael_Kirschner2 might have some thoughts on this.

1 Like

Theres a couple questions here…

No - you cannot get the results from a functionCallNode before the BuildOutputAST returns because the BuildOutputAST method is NOT an execute method.

You have to think of this function as a a compile method - it generates some code that will be run later - you are doing the work of a compiler here, transforming your node into some intermediate form that Dynamo can run. (apologies if this is clear already)

now, to get what I think you really need is this - when your node executes you need to inspect the result of the higher order function call and make some determination. To do this you need to put conditional logic into your AST.

IE -

and use that conditional to return different values depending on the evaluated value of your function call.

Okay, but what you really want is to skip all this and write a zero touch node that accepts functionObjects as inputs… unfortunately, I don’t know how to do it, we’d need to marshal between c# functions and DS function objects and AFAIK it’s not currently possible.

It might be easiest to implement your entire function in .ds so you can just call it like __apply is called and have all your conditional logic inside that function. I have not seen it done, but you should be able to include a .ds file in a package (if you are writing a package_)

4 Likes