Visual Basic Language Design Meeting Notes - 2016.05.06 #11370

Closed
AnthonyDGreen opened this Issue May 17, 2016 · 3 comments

Comments

@AnthonyDGreen
Contributor

AnthonyDGreen commented May 17, 2016

Agenda

  • ByRef Returns
  • Tuples

ByRef Returns

Today, functions and properties may only return values. This feature allows them to return references to storage locations. This is legal in the CLR with some constraints.

Proposal is that we focus exclusively on the consumption scenario. The primary use-case for declaring members which return ByRef will be specialized collection classes (such as slice) that will almost certainly be written in C# and by a small number of people. It’s very valuable for VB users to be able to use such collection types but without more compelling use cases the production scenario has too much design baggage. Specifically, how to reconcile VB’s copy-in-copy out semantics for properties passed ByRef with returning properties ByRef.

Further, I propose we add no new syntax or take a reference to a storage location, or create a ByRef local. This would limit the use of such collection types to exactly the scenarios supported today by arrays with exactly the same usage syntax.

Decisions

  • Do we need special syntax to assign a reference to a ByRef local?

No.

  • What if there’s a read-write property that takes a ByRef ‘value’ in its setter?

We never call the setter.

' This will always assign the value at the location returned from the method to the location returned from the property.
objA.ByRefReturningProperty = objB.ByRefReturningMethod() 
  • What does the IDE show when you invoke a member with a ByRef return? Maybe nothing, as we do for arrays?
' Signature of ArraySlice(Of Integer).Item
Default Public ReadOnly Property Item(index As Integer) As ByRef Integer

Something like that. The IDE team synthesizes invalid syntax for scenarios like this all the time (e.g. indexed properties in C# Metadata-as-source).

  • Passing a ByRef return to a late-bound method call?
' Where M returns a ByRef. We must copy-back.
obj.LateBoundCall(M())

Make it work.

  • Assigning a late-bound property with a ByRef return.

Doesn’t work.

Tuples

Tuples allow multiple values, potentially of different types, to be bundled up and passed around as a unit. This is primarily a platform interop feature so all decisions regarding the mutability of the tuple type, how the names of its members are persisted and read from metadata, how equality is computed, and the names and structure of the underlying tuple types are as decided in the C# LDM.

Decisions

  • What is the syntax for a tuple type with names?
Dim p As (x As Integer, y As Integer)
  • What is the syntax for a tuple type without names?
Dim p As (Integer, Integer)
  • Can I use this syntax in any ‘As’ clause where a type could appear today?
    Yes.
  • Can attributes be applied to tuple element types? If so, how would this be persisted?
Function GetFormatStringAndArgs() As (formatString As <FormatString> String, args As Object())

No.

  • When a tuple type appears as the return type of a function or property, are the names of its elements in scope inside the body of the function or property?
' Multiple return values
Function GetMinMax(numbers As Integer()) As (min As Integer, max As Integer)
    If numbers Is Nothing OrElse numbers.Length = 0 Then Return (0, 0)

    min = numbers(0)
    max = numbers(0)

    For i = 1 To numbers.Length - 1
        Dim n = numbers(i)

        If n < min Then min = n
        If n > max Then max = n
    Next
End Function

' This feels like idiomatic VB.

One major issue is that the parameter names and return variable names would share a namespace so this would be an error:

' Multiple return values
Function Pair(Of T1, T2)(first As T1, second As T2) As (first As T1, second As T2)
    Return (first, second)
End Function

' Of course, since we're adding tuples to the language no one will ever write this method.

Counter-proposal: Auto “With” the return variable so you can use .min and .max in the scope of the function.

Let's table this until we get more user/dogfooding feedback. We'll soon know whether this is essential our not.

  • How are tuple types convertible to one another?

They are element-wise convertible. The conversion is a widening conversion if and only if each element conversion is a widening conversion and narrowing if any conversion is narrowing.
Same warnings for name mix-ups, case-insensitive name comparisons (of course).

  • What's the impact on the late-binder?

We might need to update the late-binder to support tuple conversions but not for tuple names. Lot of work and so many plot holes as names are associated with declarations, not values. This will also be a problem in the debugger since tuple names won't be there at runtime in many scenarios.

  • What is the syntax for a tuple literal expression without names?
M((0, 0))
  • What is the syntax for a tuple literal expression with names?
M((x:=0, y:=0))
  • Can you specify tuple elements out of order with named arguments?
M((x:=0, y:=0))

No.

  • Do we infer names for the tuple elements as we do for anonymous type members?
Dim x = 0, y = 0
Dim p = (x, y)
' Names inferred from initializer.
WriteLine($"{p.x}, {p.y}")

No, as that would make it too difficult to make a tuple with no names.

  • How are tuple literals classified in the spec?

Initially type-less, but reclassified as values after target-typing/type-inference. Analogous to array literals.

' The type of the literal is (Integer, Integer), not (String, String)
Dim p As (Integer, Integer) = ("0", "0")
  • What should the SemanticModel.GetSymbolInfo in target typing scenarios?
' The type of the literal is (Integer, Integer), not (String, String)
Dim p As (x As Integer, y As Integer) = (x:=0, z:=0)

If you bind 'x' in the 'x:=' it should bind as a reference to the x element of the tuple, not a definition. Likewise binding 'z' should bind as would a named argument which does not refer to any parameter defined on that method. Basically, tuple expression names always refer to elements declared elsewhere, either explicitly or implicitly, but are never considered definitions on their own. This means that z:= in this case is an error since (x As Integer, y As Integer) does not define any element z.

  • Is there a special syntax for decomposing a tuple into separate variables?
Dim (x, y) = GetPoint()
For Each (x, y) In GetPoints()
From (x, y) In GetPoints()
Let (x, y) = GetPoint()
Select (x, y) = GetPoint() ' Can’t actually do this, breaking change.

' Question: Superfluous parentheses seem not very VB-ish.

Yes, but they need parenthesis. Resolves some ambiguities, such as the last example, which would be a breaking change.

  • Is there a special syntax for decomposing a tuple into separate values and assigning those values to multiple l-values?
(x, y) = GetPoint()

Probably. Though it will be the first time a statement in VB can begin with anything other than a keyword, an identifier, or a number.

  • Can operators “distribute” over tuple members provided the operator is defined on all members?
Dim location = (0, 0)
Dim acceleration = (1, 2)
For i = 1 To 3
    location += acceleration
Next
' location = (3, 6)

Cute but nope.

  • Is it OK that tuple returns don’t allow naming return values but inferring return types for functions, properties, and multi-line lambdas.
' By default in VB, you can specify the name and shape of APIs without specifying any types.
Class C
    Private V
    Property P
    Sub S(a, ByRef b, Optional c = Nothing)
    End Sub
    Function F()
    End Function
End Class

' This is already broken with Async and Iterator methods
Async Function FAsync()
Iterator Function EnumerateF()

root.ReplaceNodes(Function(original, rewritten) As (final, triviaPolicy)
                      ' ...
                      final = AddStuff(rewritten)
                      triviaPolicy = TriviaPolicy.Preserve
                  End Function)

' Do we think this might be valuable in scripting?

The (Type1, Type2) syntax is too compelling. It's much more likely to want to define a tuple with no names than one with inferred or dynamic types. 

@AdamSpeight2008

This comment has been minimized.

Show comment
Hide comment
@AdamSpeight2008

AdamSpeight2008 May 18, 2016

Contributor

@AnthonyDGreen

Other cases to consider when parsing / binding, if we use the ParseParameter method to parse them.

Dim p As (ByRef x As Integer, ByVal y As Integer)
Dim p As (x As Integer, ParamArray y As Integer())
Dim p As (x As Integer, Optional y As Integer = 1)
Dim p As (x As Integer, Optional y As Integer) ' Issue # 10293

Could we anomynous object initialization syntax?

Dim p As (x As Integer, y As Integer)
p =  New With {.X = 1 , .Y = 2 }
Contributor

AdamSpeight2008 commented May 18, 2016

@AnthonyDGreen

Other cases to consider when parsing / binding, if we use the ParseParameter method to parse them.

Dim p As (ByRef x As Integer, ByVal y As Integer)
Dim p As (x As Integer, ParamArray y As Integer())
Dim p As (x As Integer, Optional y As Integer = 1)
Dim p As (x As Integer, Optional y As Integer) ' Issue # 10293

Could we anomynous object initialization syntax?

Dim p As (x As Integer, y As Integer)
p =  New With {.X = 1 , .Y = 2 }
@AnthonyDGreen

This comment has been minimized.

Show comment
Hide comment
@AnthonyDGreen

AnthonyDGreen Jun 3, 2016

Contributor

Highly unlikely to use ParseParameter here. It's not really a parameter list.
No, I don't expect object initializer syntax will work.

Contributor

AnthonyDGreen commented Jun 3, 2016

Highly unlikely to use ParseParameter here. It's not really a parameter list.
No, I don't expect object initializer syntax will work.

@gafter

This comment has been minimized.

Show comment
Hide comment
@gafter

gafter Apr 28, 2017

Member

Issue moved to dotnet/vblang #66 via ZenHub

Member

gafter commented Apr 28, 2017

Issue moved to dotnet/vblang #66 via ZenHub

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment