dotnet / interactive

.NET Interactive combines the power of .NET with many other languages to create notebooks, REPLs, and embedded coding experiences. Share code, explore data, write, and learn across your apps in ways you couldn't before.
MIT License
2.8k stars 374 forks source link

Incorrect behaviour of methods with `params` #3448

Open TheBlueSky opened 4 months ago

TheBlueSky commented 4 months ago

Describe the bug

It is easier to describe the behaviour with code than writing 😅

  1. Create a new Polyglot Notebook (.ipynb Notebook and C# as the kernel)
  2. In the code section, paste the following code and execute it:
    void Function(params string[] args)
    {
        Console.WriteLine(args.Length);
    }
  3. Add another code section, and paste the following code and execute it:
    Function();
    Function("a");
    Function("a", "b", "c");
  4. As expected, the code should print
    0
    1
    3
  5. Now change the first code section to add a string parameter to the method, it should look like the following:
    void Function(string name, params string[] args)
    {
        Console.WriteLine(args.Length);
    }
  6. Execute the code in the first code section
  7. Execute the code in the second code section, without modifying it
  8. It prints the following, while it should raise compilation error
    0
    0
    2
  9. Restart the runtime and execute the 2 code sections
  10. It correctly raises compilation error: error CS7036: There is no argument given that corresponds to the required parameter 'name' of 'Function(string, params string[])'.

Please complete the following:

Which version of .NET Interactive are you using?

Version: 1.0.510102+2c9feb8daff54c194b638d355d18b5428480a786

Library version: 1.0.0-beta.24101.2+2c9feb8daff54c194b638d355d18b5428480a786

Build date: 2024-02-05T09:24:21.2311118Z

Screenshots

None required.

jonsequitur commented 3 months ago

This is working as intended but what's going on isn't intuitive.

C# Scripting doesn't allow you to redefine an existing method (though it does allow you to shadow one if it has the same signature). When you run the cell defining Function(params string[] args) and then again defining Function(string name, params string[] args), you're effectively adding an overload of Function. Both of them exist simultaneously in the notebook's backing compilations, resulting in this:

Function();                // calls Function(params string[] args)
Function("a");             // calls Function(string name, params string[] args)
Function("a", "b", "c");   // calls Function(string name, params string[] args)

When you restart the kernel, Function(params string[] args) no longer exists, so the call to Function() no longer compiles.

TheBlueSky commented 3 months ago

Thanks for your response, @jonsequitur, and the clear explanation. I appreciate it.

C# Scripting doesn't allow you to redefine an existing method

Could you kindly explain the reason?

jonsequitur commented 3 months ago

C# Script creates an assembly for each code submission (which in the notebook corresponds to each time you run a cell.) Later submissions don't cause the earlier submissions to be recompiled. They're immutable.

Changing a pre-existing compilation isn't allowed because it could cause subsequent assemblies to no longer be bound correctly. It would put the program into an invalid state if you could remove a declared method (or any other symbol). Removing a method is effectively what would have been happening if re-running your cell containing the Function method caused the original definition to be changed.