I suggest you ...

Allow multiple variable names with single type specifier in functin call

Current:
Public Function DoSomething(x As Integer, y As Integer, w As Integer, h As Integer, Value As Integer, Name As String, Count As Integer) As Boolean

Propsed:
Public Function DoSomething(x, y, w, h, Value As Integer, Name As String, Count As Integer) As Boolean

All variables x, y, w, h, Value would be of type Integer, like in Dim statement.

19 votes
Vote
Sign in
Check!
(thinking…)
Reset
or sign in with
  • facebook
  • google
    Password icon
    I agree to the terms of service
    Signed in as (Sign out)
    You have left! (?) (thinking…)
    PavlinIIPavlinII shared this idea  ·   ·  Flag idea as inappropriate…  ·  Admin →

    20 comments

    Sign in
    Check!
    (thinking…)
    Reset
    or sign in with
    • facebook
    • google
      Password icon
      I agree to the terms of service
      Signed in as (Sign out)
      Submitting...
      • Anthony D. Green [MSFT]Anthony D. Green [MSFT] commented  ·   ·  Flag as inappropriate

        Hi again, PavlinII,

        Thanks for all that feedback!

        Here's the reason we're considering Output at the callsite - it would allow us to infer a variable declaration inline. Today because you need to declare the variable before hand you also need to specify its type explicitly and can't use type-inference. A lot of users, myself included, love type inference and this limitation creates an inconsistent experience. With an explicit Output keyword at the call site instead of typing this:

        Dim result As Integer ' No type inference here.
        If Integer.TryParse(stringVariable, result) Then ...

        You could type:

        If Integer.TryParse(stringVariable, Output result) Then ' With type of variable inferred.
        or
        If Integer.TryParse(stringVariable, Output result As Integer) Then ' With type of variable explicit.

        Technically we could still do this without the Output keyword but it's a little harder to predict and see.

        But, even if we did add this feature we'd need to still support doing it the way you do today for back-compat reasons.

        Regards,

        -ADG

      • PavlinIIPavlinII commented  ·   ·  Flag as inappropriate

        2b) Ok, I needed to submit the answer to see what I'm missing :)

        Output keyword on declaration side may be used in any parameter. As ByRef. This is useful.
        So Output keyword should be written to every occurence on caller side.
        exitCode = Execute("cmd.exe", stdIn, Output stdOut, Output stdErr)

        Question is: Do you really need to specify Output keyword on caller side?
        Let's say it's ByRef call.
        Dim S As Stream = Nothing
        Ret = TryParse(S)
        I do not need (nor have to) specify anything.

        Let's say it's ByRef call without initialization.
        Dim S As Stream
        Ret = TryParse(S)
        Little annoying (but useful) warning is generated.

        Let's say it's Output call.
        Dim S As Stream
        Ret = TryParse(S)
        This is OK for me. Compiler recognises Output parameters. VB programmer does not need to see the "Output" keyword. It just does not generate that warning. C-like language programmer would tear his hair and evacuating this planet.

      • PavlinIIPavlinII commented  ·   ·  Flag as inappropriate

        Hello Anthony,
        thank you for reviewving this suggestion.

        1) I prefer version a). It's consistent with other rules and therefore intuitive.
        It might be usefull to be able to specify several different modifiers. But when you do so, you have to write modifier to every parameter of that sequence.

        Sub M(ByRef a, b, c, d As Integer) - version a), legal, all variables are ByRef
        Sub M(ByRef a, ByRef b, ByVal c, ByRef d As Integer) - extended, legal, every variable is described individually
        Sub M(ByRef a, ByRef b, ByRef c, ByRef d As Integer) - legal, but not smart
        Sub M(a, ByRef b, c, d As Integer) - illegal
        Sub M(a, b, c, ByRef d As Integer) - illegal
        Sub M(ByRef a, ByRef b, c, d As Integer) - illegal
        Sub M(ByRef a, b, c, ByVal d As Integer) - illegal

        2a) I do like this idea with prefix, root, suffix and Min to be Optional:
        Sub M(Mandatory As String, Optional prefix, root, suffix As String, Min As Integer)
        With rule: Everything behind single "Optional" keyword is optional.

        Optional have to specify default value:
        Sub M(Mandatory As String, Optional prefix, root, suffix As String = Nothing, Min As Integer = 0)

        Question is: What if you need different default values?
        Sub M(Mandatory As String, Optional prefix = "http://", root = "/", suffix As String = Nothing, Min As Integer = 0)
        I would consider this illegal. I do not like irregularity of '=' position.
        This should be written in long way, with single "Optional" keyword. Or optionally with many "Optional" for backward compatibility.
        Sub M(Mandatory As String, Optional prefix As String = "http://", root As String = "/", suffix As String = Nothing, Min As Integer = 0)

        I would consider illegal to specify Optional in the middle of sequence.
        Sub M(Mandatory, prefix, Optional root, suffix As String = Nothing)
        It shoudl be written like this:
        Sub M(Mandatory, prefix As String, Optional root, suffix As String = Nothing)

        I do not like out of order optional parameters where "root" is NOT optional.
        Sub M(Optional prefix, root, Optional suffix)
        This would make the code more difficult to read. It also may lead to ambigous parameter value assigning situations. You can use different overload to solve this situation.

        Same rule as 1) can be used:
        Single Optional keyword => everything behind is optional.
        Multiple Optional keywords => you have to specify Optional keyword from it's first occurence to the last parameter (like today).

        2b) I consider "Output" keyword to be similar to "ByRef" keyword. Not to "Optional" keyword.
        And all ByRef-ByVal rules can be applied to ByRef-ByVal-Output trio.
        This seems simple, short coded, non breaking and powerfull enough and to me.

        Am I missing something?

      • Anthony D. Green [MSFT]Anthony D. Green [MSFT] commented  ·   ·  Flag as inappropriate

        Hi PavlinII,

        Thanks for the suggestion. We thought pretty long and hard about this suggestion and can totally see the pros - particularly in scenarios where being concise is at a premium (like in lambdas), or where duplicate types are very common - user-defined binary operators. There is definitely precedent in the language for what this means. It also raises some interesting questions:

        1) Modifiers/Attributes:
        a) They distribute over the chain:

        ' x and y are integers, both are ByRef.
        Sub M(ByRef x, y As Integer)

        b) They don't distribute:

        ' x and y are integers, only x is ByRef.
        Sub M(ByRef x, y As Integer)

        c) They break the chain:

        ' This is illegal. x has a modifier and must also declare a type explicitly to avoid ambiguity.
        Sub M(ByRef x, y As Integer)

        The existing rules today around modifiers/attributes on fields would suggest we'd go with (a) for this.

        2) Intersection with possible (though not necessarily planned) future features:
        a) What if we let you declare Optional parameters out of order?

        ' These are all optional.
        Sub M(Optional prefix, root, suffix As String)

        But let's say you wanted only the first and last to be optional (yes, this scenario has come up) you could declare it like this:

        Sub M(Optional prefix As String, root As String, Optional suffix As String)

        Which might be fine and is probably clearer. But VB also supports a rapid-prototyping typeless style and you couldn't write this:

        Sub M(Optional prefix, root, Optional suffix)

        In fact in this mode you wouldn't be able to use any modifier without impacting any subsequent parameters in typeless mode. OK, so maybe we decide it either doesn't work in typeless mode or you're just forced to explicitly type out a type in this case.

        b) Let's say we added Output parameters to VB. C# already supports them and they're a pretty common pattern in .NET for multiple return values and the TryXyz pattern (e.g. TryParse, Dictionary.TryGetValue)

        Function Execute(path As String,
        stdIn As Stream,
        Output stdOut, stdErr As Stream
        ) As Integer

        This has the same problem as with Optionals - it's already possible today in C# to declare output parameters in any order. But like before we can require explicit typing in that edge case. Now let's look at the caller side:

        exitCode = Execute("cmd.exe", stdIn, Output stdOut, stdErr)

        Again the question of whether to distribute or not is a problem. If we don't support it you need to repeat the modifier:

        exitCode = Execute("cmd.exe", stdIn, Output stdOut, Output stdErr)

        It's odd that the use site doesn't follow the same rules as declaration, but maybe we can live with it.

        If we don't support it then the original code works but now it's impossible for an Output parameter to precede any non-Output parameters:

        M(Output x, y, Output z)

        Maybe we work around this too by letting you force ByVal and ByRef on the middle argument where it's ambiguous?

        M(Output x, ByVal y, Output z)

        This is probably rare enough that this escape hatch is good, especially for output parameters. The case may not be so cut and dry for ByRef arguments. Now arguably when the argument passing mode changes mid argument list it's probably good to be more explicit but it does mean any translation from C# code would have to potentially know that but we could flag it with a warning or something.

        Now, I'm not saying we're going to do any of these language features but it is important when deciding on any one how it might intersect with a future whole.

        What do you guys think? Do any of these possible worlds sound confusing or make you think the feature is less desirable?

        -Anthony D. Green, Program Manager, Visual Basic & C# Languages Team

      • AnonymousAnonymous commented  ·   ·  Flag as inappropriate

        @PavlinII : Yeah I Agree with you, it's make uneasy to read when the lines meet upper than 10000 line code. When I create a long parameters for function for example 12 / 13 parameters, I create a structure or class, but if your suggest was available in next release vs, it's useful .. ;-)

      • PavlinIIPavlinII commented  ·   ·  Flag as inappropriate

        Anonymous: Last line of my comment from October 27, 2011:

        > - I could use VB6 notations using %, @, ! and other special characters, but it does not meet my request for easily readable code.

      • AnonymousAnonymous commented  ·   ·  Flag as inappropriate

        Public Function DoSomething(x%, y%, w%, h%, value%, name$, count%) As Boolean
        Return True
        End Function

        It's more simple and ready to use..

      • PavlinIIPavlinII commented  ·   ·  Flag as inappropriate

        You did not understand that this proposal extends current state and does not remove anything.

        Current longer syntax have to be preserved of course.

      • PavlinIIPavlinII commented  ·   ·  Flag as inappropriate

        Nick Roberts:
        DoSomething with x, y, w, h is just an example to illustrate the issue. You can always refactor many arguments into DoSomething(Args As DoSomethingArumentsWrapper) of course.

        But refactoring adds even more code and forces consumer of function to wrap arguments - add unnecessary wrapper creating code when more useful overload member is available.

        Check Rectangle class constructor. location+size constructor is available. And x+y+width+height is available as well. Selection depends on specified scenario.

        My original suggestion would save some code in scenarios when:
        - you just have more arguments of same type
        - you want to provide suitable overloasd (location+size and x+y+w+h)
        - you have more arguments of same type that can't be grouped into meaningfull wrapper (SHA as you mentioned or: From, To, CC, BCC, ReplyTo, Subject, Body etc.)
        - you do not want to write argument wrapper for small private helper functions
        OR
        - when you're writing that wrapper and you want it to have useful constructor ;)

        Inside that wrapper would be for example (prefixes ommited):
        Private x, y, w, h, Value As Integer
        And would just copy paste this into constructor parameters. Now, you have to copy it and add 4x As Integer clause.

        Even Point and Size2D constructors could use suggested feature.

        I think it might be very useful.

      • Nick RobertsNick Roberts commented  ·   ·  Flag as inappropriate

        Hmmm. In your example (perhaps this is not fair) I would rather write:

        Structure Point
        Public X As Integer
        Public Y As Integer
        Public Sub New(X As Integer, Y As Integer)
        Me.X = X
        Me.Y = Y
        End Sub
        End Structure
        Structure Size2D
        Public Width As Integer
        Public Height As Integer
        Public Sub New(Width As Integer, Height As Integer)
        Me.Width = Width
        Me.Height = Height
        End Sub
        End Structure
        Public Function CanDoSomething(TopLeft As Point, Size As Size2D, Value As Integer, Name As String, Count As Integer) As Boolean
        ...
        End Function

        Or better still:

        Public Function CanDoSomething(TopLeft As Point, -- top left corner of box
        Size As Size2D, -- width and height of box
        Value As Integer, -- median value expected
        Name As String, -- name of division to search
        Count As Integer) -- number of sub-boxes within box
        As Boolean

        In other words, I don't think this suggestion is likely to be genuinely useful very often.

        Occasionally it might be. E.g. a function that computes SHA takes eight parameters the same type (if I remember correctly).

      • PavlinIIPavlinII commented  ·   ·  Flag as inappropriate

        Nebire:
        Private Const x As ULong = 18446744073700000000UL
        This line works, of course. But UL suffix is declaring literal value type. My suggestion was about variable name/type declaration.

        It's different scenario than my original suggestion, but I think our misunderstanding is clear now.

      • NEBIRENEBIRE commented  ·   ·  Flag as inappropriate

        Then, how you define any like this:

        This line give error:
        Private Const x As ULong = 18446744073700000000

        This line give it's ok:
        Private Const x As ULong = 18446744073700000000UL

        This function need special chars for correct values. If you retire UI from 0 give error, if you retire 0, give error overflow in both cases.

        ' convert 4 bytes from an array to uinteger.
        Private Function Convert(ByRef Bytes() As Byte, ByVal Index As UInt32) As UInteger
        Return ((0UI Or Bytes(Index)) Or (0UI Or (Bytes(Index + 1)) << 8) Or (0UI Or (Bytes(Index + 2)) << 16) Or (0UI Or (Bytes(Index + 3)) << 24))
        'End Function
        dim X as UInteger = 0UI
        Does work for you, want or not want...

      • PavlinIIPavlinII commented  ·   ·  Flag as inappropriate

        NEBIRE: I'ts far from what I want. I want the code to be read easily.
        - hUL does not work for me.
        - I could use VB6 notations using %, @, ! and other special characters, but it does not meet my request for easily readable code.

      • NEBIRENEBIRE commented  ·   ·  Flag as inappropriate

        You may declare variables with type letters, like vb6.
        So: 'hUL' it's 'h as ULong' ... Don't is very different from you want.

      • PavlinIIPavlinII commented  ·   ·  Flag as inappropriate

        Adding {} is not good idea.
        - variables are declared here like Dim statement. And Dim statement's convention does not use {} in variable list.
        - variable names are not collection initializers in this case
        - {} are not necessary and does not add new information nor differentiate any other situation.

      • Adam SpeightAdam Speight commented  ·   ·  Flag as inappropriate

        I think this is good idea, be using the array literal syntax as way to group the parameter names
        Eg
        [code]
        Public Function DoSomthing( {x,y,w,h} As Integer, {ForeName, Surname } As String ) As Boolean
        [/code]

      Feedback and Knowledge Base