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

Introduce Code Rewrite Rule for Optional Parameter #376

Open
xieguigang opened this issue Jan 20, 2019 · 7 comments
Open

Introduce Code Rewrite Rule for Optional Parameter #376

xieguigang opened this issue Jan 20, 2019 · 7 comments

Comments

@xieguigang
Copy link

xieguigang commented Jan 20, 2019

Applying the optional parameter its default value have a limiting condition in current .NET runtime: its value should be a constant value. So that we are unable using an expression(example like function calls) as the optional parameter its default value, because the expression produce runtime value.

This restriction makes the coding in current version of VB language inconvenient usually. For break such inconvenient restriction, we have some candidate options like:

The typescript language is a kind language that similar to VB language. The typescript language is also have the optional parameter for its function, example as:

module codeCompare {

    export function Foo(input: CSS = defaultStyle()) : string {
        // Optional parameter will be rewrite as 
        // if (input === void 0) { input = defaultStyle(); }
    }

}

When we run the javascript compiler, then some interesting things happened, the illegal part of the code in javascript will be rewrite as:

function Foo(input: CSS) : string {
    if (input === void 0) { 
        input = defaultStyle(); 
    }

The VB code is also have some situation like:

Module CodeCompare

    Public Function Foo(input As CSS = defaultStyle()) As String 
    End Function

End Module

The VB code that show above is illegal at current:

' Not Allowed
Public Function Foo(input As CSS = defaultStyle()) As String 

But we usually rewrite the code as below:

' Allowed
Public Function Foo(input As CSS = Nothing) As String
    If input Is Nothing Then
        input = defaultStyle()
    End If 

    ...

So by borrowing this advantage idea from the typescript compiler, rewrite rule in VB code would be something like:

For Expression: argName As Type = <DefaultValue>

If Type Is Primitive|Enum OrElse (<DefaultValue> = Nothing) Then
    Do Nothing
Else If <DefaultValue> Is Expression Then
    Rewrite As
        ```
            If argName Is Nothing Then
                argName = <DefaultValue>
            End If
        ```
End If
' Change current compiler workflow:
Code Analysis => Code Optimization => Finally IL code

' To
Code Analysis => Code Rewrite and Optimization => Finally IL code

For the situation of primitive type or non-primitive type using Nothing as default value, nothing changed in the code rewrite output:

input As Integer = 9 ' Nothing Changed
input As Types = Types.Integer ' Types is an Enum type, Nothing Changed
input As Integer() = Nothing ' The default value is Nothing, Nothing Changed

But when the default value is not an constant expression:

' optional value is an expression
input As Integer() = {1, 2, 3}
input As Integer? = Math.Pow(3, getPower())

Then it could rewrite as:

  1. For the situation of non-primitive type, if the type is a reference type:
' Sub Foo(input As Integer() = {1, 2, 3})
' will rewrite As
Sub Foo(input As Integer() = Nothing)

    If input Is Nothing Then
        input = {1, 2, 3}
    End If

    ...
  1. For the situation of primitive type or enum/structure type, it should tag a ? symbol to make it as a nullable type:
' Sub Foo(input As Integer? = Math.Pow(3, getPower()))
' will rewrite As
Sub Foo(input As Integer? = Nothing)

    If input Is Nothing Then
        input = Math.Pow(3, getPower())
    End If

    ...

' Syntax error
Sub Foo(input As Integer = Math.Pow(3, getPower()))
@jrmoreno1
Copy link

jrmoreno1 commented Jan 21, 2019

Given the way parameters are passed, and default parameters differ between javascript and vb.net, that can’t work, at least not as described.

What could work, would be to have static method with a munged name and some attribute, which could be called to produce the optional value. Not really sure what the benefit would be..

@franzalex
Copy link

@jrmoreno1 I think you're not getting the concept @xieguigang is proposing.

Currently, the default value of an optional method parameter is limited to constant values only. So you cannot write code like this:

Public Sub DoSomehing(Optional value As MyClass = New MyClasss())
    ' method code here
End Sub

This is flagged because New MyClass() is not a constant. The workaround is to rewrite the code as such:

Public Sub DoSomething(Optional value As MyClass = Nothing)
    ' check, and then initialize the optional value if the caller did not pass a value
    If value = Nothing Then value = New MyClass()

    ' continue as normal
End Sub

The proposal is to have the IDE not flag Optional value As MyClass = New MyClass() as an error and then have the compiler generate code like in the second code fragment I gave.

I cannot speak for every developer out there but if implemented, this would certainly be a useful feature to me as I often encounter this issue in both VB.NET and C#.

@reduckted
Copy link
Contributor

@franzalex I think what @jrmoreno1 is referring to is that in .NET, the value for optional parameters are inserted at compile time, whereas in TypeScript/JavaScript they are resolved at runtime. So in JavaScript, you can tell whether a value was actually passed for the optional parameter, whereas in .NET, you're always given a value for that parameter. There's two issues I can see with this.

First, using your example function, if I call it like this:

DoSomething()

Then it would be expected that value ends up as a new instance of MyClass, because that's the default value for that optional parameter.

But, if I actually specify Nothing as the value for that parameter:

DoSomething(Nothing)

Then it could be thought that value will be given a value of Nothing, when in actual fact the DoSomething() method will change that value of Nothing into a new instance of MyClass.

So there could be some confusion because value becomes New MyClass() when I don't specify a value, but when I do specify a value, and that value is Nothing, then it still gets changed to New MyClass(). I keep emphasizing "could" because this is only a shortcut to defining a default value of Nothing and having the body of the method convert Nothing to the real default value, so the signature of the method would still appear as Optional value as MyClass = Nothing. As long as you're aware of that, then it's no different to what we would do today to get around this limitation, so this is a minor issue.

Now here's the bigger issue. It doesn't work for structures. Consider this:

Public Sub DoSomehing(Optional value As Date = #2019-01-21#)
    ' method code here
End Sub

' Converted to:

Public Sub DoSomething(Optional value As Date = Nothing)
    If value = Nothing Then value = #2019-01-21#
    ' method code here
End Sub

If I don't pass a value for the parameter then I get #2019-01-21# as expected. But Nothing is the equivalent of Date.MinValue, so if I try to call this method as:

DoSomething(Date.MinValue)

Then value still ends up as #2019-01-21#, and that is not what I would expect. The only way to work around this is to use a Nullable(Of Date) instead of just a Date.

I do like this idea, but the fact that it won't work with structures is a problem.

@jrmoreno1
Copy link

@reduckted: exactly, although it’s actually the same issue for both classes and structures. The problem of initialization could be solved with a new keyword which instructed the compiler to create a new value using the default (parameterless) constructor at the call site. That wouldn’t work for interfaces or classes without a public parameterless ctor.

If you just want to have the default value replaced with a new value, that’s just a tiny bit of boilerplate that isn’t really compelling. I find that most of the time when I am adding an optional parameter it is because I am refactoring, and adding that bit of code is effectively the same thing — I don’t have any places where it is being called with nothing, so I just add the creation to the method myself. And most of the time it is more complex than a simple new T().

@pricerc
Copy link

pricerc commented Jan 22, 2019

This is a discussion about optional parameters, which presumably means sometimes calling a method without specifying a value, and sometimes with, but resolvable at compile time.

An explicit value should never be treated the same as a missing value.

Ahhh, for an IsMissing(parameterName) function....

Is there some way that Missing.Value could be harnessed to assist with this? (I don't know how it gets used at the moment, except from the reflection example in the online help).

For trivial cases, you could decompose methods into overloads, but not so much for methods with many optional parameters.

@jrmoreno1
Copy link

@pricerc: resolved at compile means that the missing parameter is added by the compiler, after that since there isn't a IsMissing function, from the point of view of the method with the missing parameter, missing and provided parameters are indistinguishable. Now that you've pointed it out, I wonder if the Missing.Value isn't what keeps classes from working with anything except a null value.

@KathleenDollard
Copy link
Contributor

Possible use for #282

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

6 participants