Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problems with MODULE.FUNCTIONNAME When Sharing Lambdas #17

Open
jonwittwer opened this issue Jan 5, 2024 · 4 comments
Open

Problems with MODULE.FUNCTIONNAME When Sharing Lambdas #17

jonwittwer opened this issue Jan 5, 2024 · 4 comments

Comments

@jonwittwer
Copy link

I want to create a library of Lambda functions to share and after experimenting with various ways of sharing, I'm running into problems when importing from a Gist.

When you import a Gist into a new module, the user can name that module whatever they want, and then the functions within that module are automatically named MODULE.FUNCTION. For example, the user creates a module named MATH and imports a Gist containing a function named MYFUN. The name that Excel Labs creates in the Name Manager is MATH.MYFUN. This is of course already documented, but creates a couple of problems:

Problem 1: If any of the other functions contained within the Gist use MYFUN, those functions are now broken. A possible solution would be to have Excel Labs automatically refactor the function names. Meanwhile, the best solution I can think of is to warn users to NOT import the Gist into a new module. The not-good solution (because it means duplication of code which defeats the purpose) is to avoid calling any named lambda functions within other lambdas.

Problem 2: Functions that use recursion, calling themselves by name, will become broken.

Solution: Wrap the recursive function with LET like this example (this function stacks an array n times - something you can do with REDUCE, but shown here for the sake of an example):

EXAMPLE = LAMBDA(param_1,param_2,
LET(
recurseFun,LAMBDA(SELF,param_1,param_2,[i],[accumulator],
IF( ISOMITTED(accumulator),
IF( IF(ISOMITTED(i),1,i) >= param_2,
param_1,
SELF(SELF,param_1,param_2,i+1,param_1)
),
IF( IF(ISOMITTED(i),1,i) >= param_2,
accumulator,
SELF(SELF,param_1,param_2,i+1,VSTACK(accumulator,param_1))
)
)
),
recurseFun(recurseFun,param_1,param_2)
));

Problem 3: If I create functions named ODE.MYFUN or L.MYFUN, and these are imported into any new module, they result in errors. I think this is an AFE issue because MODULE.ODE.MYFUN is still a valid lambda name. But, the AFE considers it an error if ODE.MYFUN is contained within a module named MODULE.

Solution: Avoid using periods in lambda names, to avoid conflicts with parameter names and how the AFE works.

@jack-williams
Copy link
Contributor

For problem 1. would you be able to provide a self-contained gist that demonstrates the problem? Name resolution in modules allow a name to refer to another name within the same module, regardless of the module name. For example, the module:

FUN_1 = LAMBDA(x, x + 1);

FUN_2 = LAMBDA(x, FUN_1(x) * 2);

Within FUN_2 the name FUN_1 will always refer to the definition above, even if you give the module a new name. When we save the module out, we add the prefix, but you do not need a prefix within the module.

For problem 2, similarly, is there a gist I can import that shows the error? You can define a name like:

TO_ZERO = LAMBDA(
    x,
    IF(x = 0, "done", TO_ZERO(x - 1))
);

And the reference to TO_ZERO should get the right prefix added when saved out.

For problem 3, yes this is a limitation / by design in AFE. In general, we only allow dot's to be in top-level name to avoid complex resolution cases such as a.b.c which could mean get c from a.b, or get b from a and then get c from the result.

@jonwittwer
Copy link
Author

jonwittwer commented Jan 9, 2024

Hi Jack, thank you for your reply. My mistake. After trying your examples, I figured out that I was doing a couple of things wrong:

  • My main issue was that I was confounding problems 1 & 2 with problem 3. Now that I've fixed the naming to avoid any periods, the behavior is as you have described.

  • I was using parameter names that were the same name as the module. For example, using "s" as a parameter name within a function results in an error if the module ends up being named "S". This was happening because I was using abbreviated module names like M, S, A, etc.

Example: Load the following gist (your examples) into a module named "X"
https://gist.github.com/jonwittwer/54d28ec2834a4cfd3d4f491bbc426c51

I'm not sure how to get around this second issue if a person happens to name their module the same thing as one of the parameters in a function, but at least the main issue can be avoided by not using periods in lambda names.

Thanks!

@jack-williams
Copy link
Contributor

The second bullet is a design limitation in AFE, Excel, and how formulas are saved. We prevent it and flag an error, otherwise the formula would not work. We do raise errors more than we need to, so in that sense there is a 'bug'.

The reason why we flag the error is as follows. Suppose we have a module called table and we define a function that defines a parameter called table. It could be a let or lambda parameter.

localFunction = LAMBDA(x, x + 1);

myFunction = LAMBDA(table, 
    table + localFunction(10)
);

When we save myFunction into the name manager it will look like:
LAMBDA(table, table + table.localFunction(10)). Note that we prefix the local function now with the module name.

Unfortunately, this will not work correctly. The presence of the parameter table from the LAMBDA means that table.localFunction will be evaluated as "lookup field localFunction on the function input table" rather than "return the formula named table.localFunction"

My only suggestion and workaround is to prefix LET or LAMBDA parameters with underscore to make them different from module names.

@jonwittwer
Copy link
Author

Thank you. In my documentation, I think I will also suggest a few safe names for the module in case they want to import to a separate module.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants