twinbasic / lang-design

Language Design for twinBASIC
MIT License
11 stars 1 forks source link

Bind variable in `With` block to a name #57

Open Greedquest opened 1 year ago

Greedquest commented 1 year ago

Motivation

I use With blocks like context managers to ensure a temporary object gets cleaned up when I'm done with it. With a regular With block I often find I would like to refer to the variable itself particularly when passing it to some sub-function. E.g.

With New Scripting.FileSystemObject
    Dim files As Variant = .Find("*.xlam")
    doMoreProcessing(files, /* pass fso here */)
End With

You often see this pattern with .Self

With New FileWriter("C.txt")
    'do stuff
    writeHeader .Self
    writeBody .Self
End With 'closes file

Of course that only works if the object is under your control.

If I declare the variable eagerly

Dim fso As FileSystemObject = New FilesystemObject
With fso
    Dim files As Variant = .Find("*.xlam")
    doMoreProcessing(files, fso)
End With

Then we don't actually clear up the object on End With which defeats the purpose.

Suggested Syntax

With New Excel.Application Alias app
    app.Name
End With

With CreateObject("Excel.Application") Alias app
    app.Name
End With

Note I use CreateObject in these examples but really any expression that returns an Object would be allowed

Semantics

The scope of the name app will be only within the With block. Also once Alias'ed the variable cannot be used anonymously with just .foo

With New Excel.Application Alias app
    app.Name 'fine
    .Name 'now an error with the alias specified the variable name must be used
End With
app.Name 'not fine, app was defined in the block only

Nested with blocks get access to all their parent variables

With Sheet1.Range("A1") Alias cellToFill
    With cellToFill.Interior Alias cellInterior
        cellInterior.ColorIndex = Abs(cellToFill.Value)
        cellInterior.InvertIfNegative = True
    End With
    processMore cellToFill
End With

Alternatives Considered

With app As New Excel.Application 'confusion with auto instantiated variable
    app.Name
End With

With app As CreateObject("Excel.Application") 'confusion as we expect to see a type here, not an expression
    app.Name
End With
With New Excel.Application As app 'reads nicely but inverted compared to what we usually see
    app.Name
End With

With CreateObject("Excel.Application") As app 'looks quite nice but inverted as above
    app.Name
End With
With app As Excel.Application = New Excel.Application 'quite verbose
    'plus, in standard With blocks the type of the expression is always known so shouldn't be specified manually
    app.Name
End With

With app As Object = CreateObject("Excel.Application") 'again the `As Object` gives no information
    app.Name
End With

'Also those 2 forms imply the existence of a late bound implicit variant form:
With app = CreateObject("Excel.Application") 'this form is ambiguous, it could evaluate to boolean
    app.Name
End With
wqweto commented 1 year ago

Keep in mind that the implicit variable has a reference type the compiler is aware of. It's not a Variant and it's not simply an Object as it inherits the data-type of the expression used in With initialization.

I would not introduce an explicit alias for the hidden variable but allow addressing it with some new/weird syntax something like .0 for current With block variable and .1 for parent one etc. and keep "bare" syntax of .Prop/Method executing fast i.e. try to keep the implicit variable in a register for fast(er) member access.

mburns08109 commented 1 year ago

@wqweto,

.0, .1, etc.? Oh I hate that idea - it smacks too much of "secret code magic" stuff!

How about something more intuitive and within a WITH block like ".With" maybe if we really need to go this direction (which, I can admit, will allow some easier custom cleanup of COM Refcount issues in some circumstances - especially for imported VB6 legacy code)?

If there is a need to access the implicit With variable for surrounding/nested With...End With blocks, well you're just going to need to declare them and use actual variables for that. Don't be so danged lazy about this! Remember KISS principles? We hold to those for a good reason in most circumstances. Why is it that the first answer always seems to be "make the magic happen for me"? Sorry, but coding is work. Declare a damned variable if you need to use it in a nested With---End With structure.

This is BASIC, not a C++/C# variant language!

mwolfe02 commented 1 year ago

A couple of other alternatives:

With New Excel.Application Dim app
    app.Name
End With

With CreateObject("Excel.Application") Dim app
    app.Name
End With

Alias is not bad, but I think the Dim keyword is more semantically similar to what we're actually doing here.

If twinBASIC adds support for block-level scoping, I think the Local keyword works even better:

With New Excel.Application Local app
    app.Name
End With

With CreateObject("Excel.Application") Local app
    app.Name
End With
mansellan commented 1 year ago

I would be strongly against any syntax that puts the type ahead of the variable. Nothing else in VBx does this:

With New MyClass Local x

It's unconventional, and requires a different mode of thinking. It's cognitive friction.

With app As MyClass = New MyClass()

(parens optional) may be verbose, but it fits with the language, including the new As New vs = New syntax.

It can cope with constructors:

With app As MyClass = New MyClass(1, "Products")

Further, in v.Next, with type inference, it could evolve to:

With app As MyClass = New(1, "Products")