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:
- Custom twinBASIC IDE Installation Guide (NOTE: the twinBASIC VSCode extension is under a feature freeze until 2023)
- GitHub Issue Tracker (report bugs)
- twinBASIC Discord Server (chat about the project)
- twinBASIC/VBx LinkedIn Group
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-inVBA.Collection
or reference an external libraryScripting.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 addTB_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.
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 ]