twinbasic / lang-design

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

Shared declarations #41

Open mansellan opened 2 years ago

mansellan commented 2 years ago

Is your feature request related to a problem? Many languages (including VB.Net and C#) offer the opportunity to define class members that are available on a shared basis, rather than per-instance. In VB.Net, these are Shared members, which are broadly equivalent to static declarations in C#.

This is useful for utility functions, as it's more tightly scoped than simply using a standard module. It could go some way to alleviating the problems of not (yet) having namespaces available in tB.

Describe the solution you'd like


Public Shared Class Math ' Only Shared members allowed in a Shared class.

  ' Example usage: Dim biggest As Long = Math.Max(1.1, 1.2)
  Public Shared Function Max(first As Double, second As Double) As Double
     If first > second Then
        Return first
     Else
        Return second
     End If
   End Function
End Class

Describe alternatives you've considered Standard modules, but they're not as flexible. Whilst you can prefix the function name with the module name, it's entirely optional. Public functions of standard modules always go into the global namespace. Shared functions are "carried with" the class in which they are defined.

Additional context Shared / static members are also the foundation of other language features in VB.Net / C#, such as operator overloading (including casting) and (in C#) extension methods.

Note that in VB.Net, the Shared operator cannot be applied to classes, only to members. In C#, classes can be marked as static, which means they can only contain static members. I think the restriction in VB.Net is artificial, probably to avoid confusion with standard modules. IMO, twinBASIC should allow Shared-only classes, they might even end up being generally preferred over standard modules by some.

I seem to recall mention of this, but couldn't find an issue for it yet.

bclothier commented 2 years ago

I should point out that even in VBA, you already can approximate this with a predeclared class module. However, the main difference here is that a static1 class cannot be instantiated whereas a predeclared class can be also instantiated.

In VB6, you'd use a GlobalSingleUse instancing. However because it's global, I think (but may be wrong) that it also implies appobject behavior in which case it's no better than a standard module in terms of namespace pollution.

I suspect that the easiest way to handle this as well as the other instancing is to provide an attribute [Instancing(...)] and creating a new instancing type in the addition to the six that VBx uses, which can be called Static. No new keywords, no confusion, everything's still text, so happy everyone. (right?)


1. I hate the keyword Shared -- it's not very helpful in describing what it means. A unfortunate consequence of VBx's using of Static to mean something completely differently and needing to disambiguate the concepts here.

WaynePhillipsEA commented 2 years ago
  1. I hate the keyword Shared -- it's not very helpful in describing what it means. A unfortunate consequence of VBx's using of Static to mean something completely differently and needing to disambiguate the concepts here.

Me too, it's very unfortunate as Static is much more widely used for this feature in other languages.

wqweto commented 2 years ago

Shared / static classes in other languages (like C#) are there because of lack of standard modules as in VBx as far as I understand.

In VB6, you'd use a GlobalSingleUse instancing. However because it's global, I think (but may be wrong) that it also implies appobject behavior in which case it's no better than a standard module in terms of namespace pollution.

3 - SingleUse and 4 - GlobalSingleUse are available only in Ax-EXE project while for Ax-DLL ones there is only 1 - Private, 2 - PublicNotCreatable, 5 - MultiUse and 6 - GlobalMultiUse.

Instancing property maps to several VB_Xxx attributes which have more combinations possible than the entries available in the combobox and the Instancing property is not even available for Std-EXE projects where every class in just 1 - Private instancing.

Here is a list of all Instancing values possible against VB_Xxx attributes it sets

Instancing MultiUse VB_GlobalNameSpace VB_Creatable VB_PredeclaredId VB_Exposed
1 - Private -1 False False False False
2 - PublicNotCreatable -1 False False False True
3 - SingleUse 0 False True False True
4 - GlobalSingleUse 0 True True False True
5 - MultiUse -1 False True False True
6 - GlobalMultiUse -1 True True False True

Apparently there is no way to set VB_PredeclaredId using Instancing.

What I just found out it that it's possible to set VB_PredeclaredId = True simultaneously with VB_GlobalNameSpace = False which requires calling routines on the global object through its class name i.e. only MyClass.TestGlobal works while TestGlobal fails compilation as the methods are not available in the global namespace.

Nothing prevents instantiating a 6 - GlobalMultiUse class in addition to the global instance provided unless this is prevented with some code in Class_Initialize like this

Private Sub Class_Initialize()
    If Not Me Is Class1 Then
        Err.Raise vbObjectError, , "Singleton instantiation attempted"
    End If
End Sub

I'm a big proponent of VB_GlobalNameSpace = False for standard modules as this would allow not polluting global symbols table with for instance FFI Enums or API declares e.g. calling Win32.Sleep as a global API declare that does no appear as Sleep in global namespace.

bclothier commented 2 years ago

Nothing prevents instantiating a 6 - GlobalMultiUse class in addition to the global instance provided unless this is prevented with some code in Class_Initialize like this

I used to do that but I came across a situation in VBA where that doesn't work and instantiating still happens anyway. I cannot remember the particulars. In other languages there are additional considerations for handling access. This old article for C# shows that naive implementation can be problematic. This does not apply in VBx because it's normally single-threaded but if tB has multi-threading, that will require some consideration.

I'm a big proponent of VB_GlobalNameSpace = False for standard modules

To clarify, is that an existing feature? I don't see VB_GlobalNameSpace attribute when exported from a VBA module. Not sure if VB6 treats it differently. Btu I very much agree with your sentiments and would much rather have Win32.Sleep and not just Sleep.

mansellan commented 2 years ago

Interesting, perhaps that's why Shared Class is not a thing in VB.Net, it's just not needed.

I was mainly asking about Shared class members though - Subs, Functions, Properties, Fields etc. It can be useful for a class to have a mixture of static and instance declarations.