dotnet / vblang

The home for design of the Visual Basic .NET programming language and runtime library.
289 stars 65 forks source link

[Proposal] improvements the ! operator for the anonymous variable #129

Open xieguigang opened 7 years ago

xieguigang commented 7 years ago

Currently in VisualBasic, that we can using the ! operator for the default index property. When the ! operator combine with the VisualBasic With anonymous variable syntax, it give us too much convenient in the data analysis programming:

.GetValue("value") = 123
' which is equivalent to the code
!value = 123

Using ! operator makes our code more brief as it let us without typping the additional GetValue name when it combine with the With anonymous variable syntax

But in a more common scenario, we using a string variable instead of the string constant, like the example as:

For Each group$ In {}
    .GetValue(group) = .GetValue(group) * 10
Next

As the ! operator just enable applied on the string constant, so that in such scenario, we are not able using !group for getting the value any more, as the code !group it means .GetValue("group"), not means .GetValue(group).

Due to the reason of we are using () brackets for passing the variable parameter, () brackets which have a underlying meaning of Evaluation, so that I think that we can using syntax !(group) for the string variable and !group for a string constant, as the !(...) means evaluate the variable.

The ! operator combine with the With anonymous variable syntax is a common scenario and much useful language feature in some data analysis application, example like the source code:

' Creates an anonymous variable
With New EntityObject(name)

    For Each group As String In groupLabels.Keys
        Dim experiment = totals(group)
        Dim relative# = experiment.proteins(protein.ID) / experiment.total * 100%

        .ref(group) = relative
    Next

    !AVERAGE = (Aggregate s In .Properties.Values Into Average(Val(s)))
    !uniprotID = protein.ID
    !fullName = uniprots(protein.ID) _
    !fullName

    ' reference the anonymous variable its original source
    relativeAmounts += .ref
End With

''' <summary>
''' Extension method for VisualBasic ``With`` anonymous variable syntax source reference helper
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="x"></param>
''' <returns></returns>
<Extension> Public Function ref(Of T)(x As T) As T
    Return x
End Function

Allows using the !(...) for the index property using a variable will makes the code more better:

For Each group As String In groupLabels.Keys
    Dim experiment = totals(group)
    Dim relative# = experiment.proteins(protein.ID) / experiment.total * 100%

    .ref(group) = relative

    ' In a more brief way
    !(group) = relative
Next
AnthonyDGreen commented 7 years ago

Has extracting the keys to constants been very beneficial to your team? Have you needed to change the keys often enough that the extra level of indirection has paid off?

xieguigang commented 7 years ago

Using a constant string directly have some disadvantages:

  1. Using the constant string for the index property through ! operator didn't have well supports from the VisualStudio IntelliSense
  2. The string constant is case sensitive, so that this may caused trouble if we are careless with our eyes:
    ' They are similar, but they are totally different things
    !key_i = True
    !key_I = False
  3. As this feature didn't have well supports in the VisualStudio, so that if we want to modify a key name, we will be mad if the key name appears in many places. Because we can't modify it as we can modify the variable name at once.
  4. Only the string variable can deal with the different user inputs, the constant string can not mutate for deal with the difference user inputs.

So that, I would be prefer using a string constant variable or variable instead of directly using the constant string:

Const label1$ = NameOf(label1)

With New Dictionary(Of String, Foo)
    ' Using property name is too much words
    .Item(label1) = ...

    ' directly using a string constant is too danger
    ' and with very limited function
    !label1 = ...

    ' Using variable is safe and brief code
    !(label1) = ...

    ' can deal with different user inputs
    For Each groupName$ In userInputs
        ' probably the variable in !(...) is not limited to string variable,
        ' it can be compatible with integer, long, char, etc.
        !(groupName) = ...
    Next
End With

Using the With anonymous variable not only because of this is the VisualBasic exclusive language feature, a legacy from VB6, and also it can makes us benefit of focus-coding, as all we knows that in current With code block the main character is him, all the code function we've implements is all about this variable that "non-existent". When we have done the code and we are still able to get back its original source value by using an extension function like:

<Extension> Public Function ref(Of T)(x As T) As T
    Return x
End Function

But when the index property combined with this anonymous variable, it makes things get worse: for using the index property, we have to writing the additional property name like .Extensions in this code example:

https://github.com/xieguigang/GCModeller/blob/fb57f8e70f5306807f5a54ff24a3cd01a840deb1/src/GCModeller/models/Networks/Network.BLAST/Metagenome/Protocol.vb#L119

So, if we are able using the ! operator for the string variable, then the things goes easy:

For Each hit As BlastnMapping In source
    With hit
        ' inline value assign of the 18s rRNA taxid
        If (taxid = taxidFromRef(hit.Reference)) > -1 Then

        Dim nodes = taxonomy.GetAscendantsWithRanksAndNames(taxid, True)
            Dim tree$ = TaxonomyNode.BuildBIOM(nodes)
            Dim name$ = taxonomy(taxid)?.name

        !(Protocol.taxid) = (taxid.value)
            !(Protocol.taxonomyName) = name
            !(Protocol.Taxonomy) = tree
        Else
            Call .Reference.Warning
            Call notFound.Add(.Reference)

            !(Protocol.taxid) = Integer.MaxValue  ' 找不到具体的物种分类数据的
            !(Protocol.taxonomyName) = "unknown"
            !(Protocol.Taxonomy) = "unknown"
        End If
    End With

    Yield hit
Next