twinBASIC Update: July 9, 2023

Highlights include a supercharged Collection class (that also replaces Scripting.Dictionary for most use cases) and several exciting community projects.

twinBASIC Update: July 9, 2023

On April 23, 2021, I helped Wayne Phillips introduce the world to twinBASIC at the Access DevCon Vienna conference.  I boldly predicted that twinBASIC (along with the Monaco editor) would replace VBA and its outdated development environment by 2025.  With that goal in mind, this weekly update is my attempt to keep the project fresh in the minds of the VBA development community.

Every Sunday, I will be providing updates on the status of the project, linking to new articles discussing twinBASIC, and generally trying to increase engagement with the project.  If you come across items that should be included here, tweet me @NoLongerSet or email me at mike at nolongerset dot com.

Here are some links to get involved with the project:


Highlights

Collections Get a Boost

The Collection object is one of the few built-in data structures in VBx.

Unfortunately, it's a bit underwhelming both in its performance and its completeness.  One such glaring omission is the lack of an Exists method.  This missing function leads to a whole pattern of problems most easily solved through "programming by exception" (itself a code smell).  

For example, here's an excerpt from my article on extracting field descriptions from a table:

The problem is that the DAO field object does not have a Description property.  Rather, the field object has a Properties collection, of which "Description" may or may not be an item in that collection.

Furthermore, the Properties collection has no Exists method to check for the existence of a particular property.  Our only option, then, is to use an On Error Resume Next statement, attempt to access the property, and then see what happens.

Here's the accompanying code:

Function ExtractFieldComments(td As DAO.TableDef) As Dictionary
    Dim Dict As New Dictionary
    
    Dim Fld As DAO.Field
    For Each Fld In td.Fields
        Dim Comment As String
        Comment = vbNullString        
        
        On Error Resume Next
        Comment = Fld.Properties("Description")
        On Error GoTo 0
        
        Dict.Add Key:=Fld.Name, Item:=Comment
    Next Fld
    
    Set ExtractFieldComments = Dict
End Function

And here's how we could rewrite that code in twinBASIC with the Exists function added to the Collection object:

Function ExtractFieldComments(td As DAO.TableDef) As Dictionary
    Dim Dict As New Dictionary
    
    Dim Fld As DAO.Field
    For Each Fld In td.Fields
        Dim Comment As String
        If Fld.Properties.Exists("Description") Then
            Comment = Fld.Properties("Description")
        Else
            Comment = vbNullString
        End If
        
        Dict.Add Key:=Fld.Name, Item:=Comment
    Next Fld
    
    Set ExtractFieldComments = Dict
End Function

This code is both easier to read and more performant, as exceptions are notoriously inefficient (in relative terms).

Here is a full list of the improvements made to the Collection class this week:

  • improved: significantly more performant Collection class implementation
  • added: Collection.Exists() method
  • added: Collection.Clear method
  • added: Collection.KeyCompareMode property (vbTextCompare(default), vbBinaryCompare)
  • added: Collection.KeyCountHint property (0(default) for auto)
  • added: Collection.Keys property for exposing the internal keys as a String-array
  • added: Collection.Items property for exposing the internal items as a Variant-array

Wayne posted detailed benchmark comparisons for the Collection class (VB6 [compiled, fast] vs. twinBASIC), but here are the highlights:

  • Add item at start or end of list is slightly faster in tB (~10%)
  • Add item in random position is much faster in tB (~100x)
  • Indexed/For-Each item retrieval is close to infinitely faster in tB (~2,500x to 5,000x...VB6 performance suffers with large collections; tB does not)
  • Remove All items (forward, sequential) is about twice as fast in tB (~2x)
  • Remove All items (backward, sequential) is close to infinitely faster in tB (1,748 ms in VB6 vs. less than 1 ms in tB for 50,000 items)

As is typical, Ben Clothier summed up my thoughts on the situation more succinctly than I could:

For one, I'll be mightily glad to not have to make the painful decision between whether to use a built-in VBA.Collection or reference an external library Scripting.Dictionary or gasp handroll my own!

Wayne confirmed that was a primary consideration for him, too:

Yeah, that's exactly why I put some effort into it.  It's now high-performance for both index-based use, and key-based use, and so for 99% of use cases, it'll be the go-to (GoTo?) option

Around the Web

basicNES Runs in twinBASIC

fafalone posted the following in the Discord show-and-tell channel:

basicNES runs without modification :)
Except adding a missing const to frmAbout. (It's 1)

There's some audio glitches (you'll get a blast right at startup) and very minor flickering (that may be due to incorrect color depth issues since PictureBox.Point isn't implemented)... but... it runs!

tbShellLib Lite

Another fafalone entry in the show-and-tell channel:

The latest version of tbShellLib (released today) has 'Lite mode'. This mode excludes all API and Common Control definitions, misc definitions not used by interfaces, and the PROPERTYKEY collection.

This has a substantial impact on performance: On my system, it cuts intellisense popup times from 6s to 2s.

I know one day tB will have the performance of VB or VS making this unnecessary, but in the mean time, since a lot of people define their own APIs and don't use PKEYs, this gives a lot of people a chance to make it a lot quicker.

To use Lite mode, in a project using tbShellLib, open your Project Settings, go to 'Project: Conditional compilation arguments', make sure it's checked to enable it, and add TB_SHELLLIB_LITE = 1 and save. This impacts the IDE, not just compiled exes, so that's how the performance benefits come.

The new version, 4.12.166, is available via the Package Server when you click 'twinpack Packages' in References, or on the project GitHub: https://github.com/fafalone/tbShellLib

MScal Replacement Coming Soon

This one comes from sokinkeso:

I have created a usercontol, that mimics the behavior of MScal.ocx (calendar) control. It is self drawn, and includes only two comboboxes for the month and year selection. It also has some enhancements over VB6's calendar:

New properties:
• DayBackColor
• DaysMargin
• TitleMargin

New key controls:
• PageUp = Previous month
• PageDown = Next month
• Ctrl+PageUP = Previous year
• Ctrl+PageDown = Next year
• Home = Goto Today

Have to fix a small issue in the x64 version, and hopefully is will be out soon as a TwinBasic package..

twinBASIC IDE Sluggishness

In some cases, it seems the DEBUG CONSOLE is to blame.  

Here's a request from Wayne in Discord:

All: next time you notice the IDE becoming sluggish, do me a favour and clear the DEBUG CONSOLE and see if it improves any. I strongly suspect the DOM nodes in the debug console are hugely affecting IDE performance.

Changelog

Here are the updates from the past week.  You can also find this information by visiting the GitHub twinBASIC Releases page.

Releases · WaynePhillipsEA/twinbasic
Contribute to WaynePhillipsEA/twinbasic development by creating an account on GitHub.

WARNING: The following issue is present in BETA builds 202 - 346 (the latest build as of publication):

  • there are known memory leaks in these versions, so memory usage will be higher than normal

BETA 347

  • fixed: DEBUG CONSOLE output would sometimes overlap the timestamp on the next line
  • improved: significantly more performant Collection class implementation
  • added: Collection.Exists() method
  • added: Collection.RemoveAll() method
  • added: Collection.KeyCompareMode property (vbTextCompare(default), vbBinaryCompare)
  • added: Collection.KeyCountHint property (0(default) for auto)

BETA 348

  • fixed: Form.WindowState property behaviour not matching VB6 [ consultancy ]
  • fixed: Form.Activate event not always firing in the first instance when the form is first opened [ consultancy ]
  • improved: Clipboard.GetData()/SetData() now report an Unimplemented warning, rather than a compile error [ consultancy ]
  • added: support for Form.QueryUnload event [ consultancy ]
  • fixed: edge case of For-loop with floating point control variable and a Step value that requires FP conversion, caused FPU runtime errors [ consultancy ]
  • changed: debugger main thread stack reservation size increased to 4MB to aid in running procedures that have high stack requirements, due to non-efficient codegen that will be fixed in a later update [ consultancy ]
  • added: project setting 'Project: Build Stack Reserve Size' to change the built executable stack reserve size in the PE header
  • fixed: accessing a control via bang operator on the form instance (e.g. Form1!controlName) is now working [ consultancy ]
  • fixed: Label.AutoSize property now defaults to False, to match VB6 [ consultancy ]
  • added: support for Label.AutoSize property (runtime only) [ consultancy ]
  • added: support for Label.WordWrap property (runtime only) [ consultancy ]
  • fixed: some edge cases around control Resize events [ consultancy ]
  • improved: (IDE) increased contrast of select text back color in the Light theme [ Krool, discord ]
  • changed: Collection.RemoveAll method renamed to Clear [ Krool, discord ]
  • added: Collection.Keys property for exposing the internal keys as a String-array [ FullValueRider, discord ]
  • added: Collection.Items property for exposing the internal items as a Variant-array [ FullValueRider, discord ]

BETA 349

  • fixed: App.ThreadID defined incorrectly as a LongPtr in the interface from package 'VB' [ Krool, discord ]
  • fixed: (regression since BETA 343) errors in interface member definitions would not be properly reported in the IDE [ https://github.com//issues/1604 ]
  • fixed: ReDim <Something> statement now validates that Somthing is an array, or a Variant [ https://github.com//issues/1606#issuecomment-1623764886 ]
  • added: warning TB0016 for implicit array variable creation caused by ReDim [ https://github.com//issues/1601 ]

BETA 350

  • fixed: some warnings about ReDim without prior Dim in the built-in packages
  • added: Form.ValidateControls method implementation [ https://github.com//issues/1188 ]
  • added: support for PictureBox.AutoSize property [ https://github.com//issues/1188 ]
  • fixed: Intellisense hover over an array of type LongPtr would show as Long/LongLong [ https://github.com//issues/1607 ]

All original code samples by Mike Wolfe are licensed under CC BY 4.0