closures continued.. ByRef, ByVal and ()

Posted Sat, Apr 8 2006 21:35 by bill
 
A rarely used, somewhat obscure feature of VB, is by enclosing a variable in ()'s it is passed ByVal instead of ByRef.  Consider the following code:
 
(1)
   Dim s As String = "hello"
   Foo(s)
   Console.Writeline(s)
 
versus:
 
(2)
   Dim s As String = "hello"
   Foo((s))
   Console.Writeline(s)
 
 
where Foo is defined as:

Sub Foo(ByRef value as string)
  value = "goodbye"
End sub 
 
In second case the output is "hello", whereas in the first case it is "goodbye", all due to one little ol' set of ()'s encompassing the variable s.
 
So we do already have a mechanism to achieve the ByVal approach I discussed in my previous post about closures.  Btu the question we need to ask is "Is that desirable for VB?"
 
Let me give another example. Let's say I have a simple method as follows:
 
Sub Add100(ByRef x as Int32)
   x +=100
End Sub
 
Now in a Form's code I could call that method like
 
   Add100(Me.Left)
 
   Add100((Me).Left)
 
   Add100((Me.Left))
 
and in 2 of the above 3 cases the form would move 100 pixels to the right.  Is that desirable ?  How difficult is it to visualize the difference between those three examples and know which one is the odd one out ?
 
Now assuming this was also used to decouple local variables when in LINQ lambda expressions, then taking Paul's example:
 
        For i AsInteger = 0 To 2
            Dim y AsInteger = i
            queries(i) = Select x From x In xs Where x <= y
        Next
 
we could dramatically change the output from that by adding just one set of ()'s
 
        For i AsInteger = 0 To 2
            Dim y AsInteger = i
            queries(i) = Select x From x In xs Where x <= (y)
        Next
 
but is that just a little too obscure?  I think so.
 
I really feel the overloading of ()'s to mean so many different things in VB is one of it's major pains. This almost cryptic use of parenthesis is something I'd more expect in a punctuation pedantic language like C#, not VB.    The code needs to be more visually distinct.
 
So let's try changing that a bit, visually:
 
       For i AsInteger = 0 To 2
            Dim y AsInteger = i
            queries(i) = Select x From x In xs Where x <= (y)
        Next
 
possible a bit too subtle still. Let's try adding the ByVal keyword
 
 
       For i AsInteger = 0 To 2
            Dim y AsInteger = i
            queries(i) = Select x From x In xs Where x <= (ByVal y)
        Next
 
that certainly sticks out a bit more :)
 
Let's try it on the other examples:
 
    Add100(Me.Left)
 
   Add100((ByVal Me).Left)
 
   Add100((ByVal Me.Left))
 
 
Visually, I think adding the ByVal inside the ()'s makes all the difference.  It moves punctuation into a spoken language.  The VB language specification agrees with this type of notation saying:
 
The Visual Basic programming language has a syntax that is similar to English, which promotes the clarity and readability of Visual Basic code. Wherever possible, meaningful words or phrases are used instead of abbreviations, acronyms, or special characters.
 
 
So there we have it folks.  That's my opinion on the matter :)
 
 
 
Filed under: , ,

Comments

# re: closures continued.. ByRef, ByVal and ()

Sunday, April 09, 2006 10:59 PM by Jonathan Allen

That syntax looks pretty clean to me, and is a heck of a lot better than what we have now.

# re: closures continued.. ByRef, ByVal and ()

Monday, April 10, 2006 8:21 PM by Paul Vick

I think you're confusing two different things. In the case of expressions being passed to reference parameters, the real feature is that we allow you to pass r-values to reference parameters by creating a temporary to hold the value. This allows you to pass (x) to a reference parameter, but it also allows you to pass the literal 10 to a reference parameter. There's really nothing special about the parenthesis except that they, by their nature, always produce a r-value.

The parens don't help you in the closure case because what's being captured is the actual variable reference itself. How that variable reference gets used in an encompassing expression has no bearing on the capture, so it would actually be weird (from an internal consistency standpoint) to have parenthesis somehow "uncapture" the l-value within it. It also raises the question of what (x + y) would do -- would that uncapture both x and y?

All that being said, I don't totally disagree with the idea of having an explicit way of switching between the capture semantics, although I might argue the default should be to just capture r-values and then have an opt-in way for capturing l-values (i.e. a ByRef modifier rather than a ByVal modifier). Still, that's adding another layer of complexity, so I can't really say that we'll do something here, but it's definitely something we'll talk about...

# re: closures continued.. ByRef, ByVal and ()

Monday, April 10, 2006 8:52 PM by bill

Let me address this question first:

> It also raises the question of what (x + y)
> would do -- would that uncapture both x and y?

I think what you are sayign there is actually much of what I was sayign, in that we need to move away from the use of just ()'s. If you adopted the (ByVal o) syntax there could not be that ambiguity.
You couldn't have (ByVal x + y).
It would have to be (ByVal x) + y
Syntatx like (ByVal (x + y)) would be illegal because x + y is an expression that produces a r value already.

I definetly disagree on the idea it should be r values instead of l values by default. That's jsut going to make it more confussing. I'd like to hear your arguements why you think it should be r. I presented some in my previous blog entry on captures but came to the conclussion that l values were preferable.

As to the parens with the capture (that is parens with the ByVal keyword added <g>), I think it does actually make a difference in what the compiler can do. The ()'s means a temporary variable is created, and as such the VB compiler can treat the compiler created variable differently from the code written variable, allowing things such as escaping out of the issue of the local variables insid a loop. Because the variable is a compiler generated one, you'd know there is no need ot link that to the original code, rather you'd use it to instantiate a value in the anon delegate's generated class.

to me, the (ByVal o) means the same as *ptr instead of **ptr in the effect it has on the code.

# Parenthesized expressions (more on Closures)

Sunday, April 23, 2006 11:26 PM by @ Head

since Scott said such nice things about my last post on closures, I thought I'd add some quick references...