Cory transcribed some examples that we tried to come up with that give the wrong result: https://gerrit.wikimedia.org/r/c/mediawiki/services/function-orchestrator/+/797349/. The example corresponds roughly to the following Python code:
def h(h_x): return h_x def g(g_x): if g_x: return f else: return h def f(f_x): if f_x: return g(g_x=f_x)(f_x=False) return not f_x print(f(f_x=True))
It is currently looping forever instead of returning True. We're not 100% sure what is going on there, but we strongly suspect it to be due to problems in variable scoping.
Currently when evaluating a Z7 function call object the arguments are immediately added to the scope, before resolving the Z7K1 function part. This is incorrect because the Z7K1 function part might be a complex object that also needs to be evaluated before we get to an actual function body, e.g. with currying. The arguments don't make sense outside of the function body. We need to avoid adding the arguments to the scope until we actually start resolving the function body.
Moreover we need to modify the structure of scopes to map variable names to both an argument object and the original scope for that object, so that we can restore the scope when we retrieve the argument. The initial scope defines everything that is needed to evaluate those arguments, the things that get added to the scope while resolving the Z7k1 do not make sense for the arguments.
Not sure also if T309038 might be related, so filing a separate task for this.