VB LDM 2014-02-10

Topics: VB Language Design
Coordinator
Mar 28, 2014 at 10:18 PM
Edited Sep 10, 2014 at 5:38 PM
VB Language Design Meeting 2014-02-10

Strict Module

C# lets you have "static class" but VB only has "Module" which lifts all its members into the namespace. Discussion on this was prompted by a comment of MVP Jan Záruba (Đonny) who said
I use Partial Private Sub New to achieve the same effect as in C# with static class. The advantage of this compared to Module is that static class does not "pollute" namespaces with it's members (and I also cannot have generic module anyway)."
Đonny encountered a known and approved Roslyn breaking change, where VS2013 let you write "private partial sub new", but Roslyn doesn't. This is by design. But the question is, what could he and other users do?

Language designer Anthony D. Green looked into it:
Today VB’s modules behave very much like F#’s modules when the AutoOpen attribute is applied. I’m proposing adding a modifier “Explicit” to modules which will go the other way. Additionally, we’d considered changing the codegen of VB Modules to exactly match those of C# (both abstract and sealed) so that they could be recognized by C# in Dev10. Unfortunately this turned out to be a breaking change due to a bug in the XmlSerializer. With my proposal we could consider changing the metadata representation to match C# and could also map C# static classes back to Explicit Modules. If C# does end up accepting the static import feature but retains the restriction to static classes it’ll be important to have a way for VB code to easily create such things – otherwise C# won’t be able to consume them (e.g. the Roslyn VB SyntaxFactory module).

IL

C# static classes are currently emitted as .class private abstract auto ansi sealed beforefieldinit
VB modules are currently emitted as [StandardModule] .class private auto ansi sealed

Language design

  • How do you declare something whose name is hidden and whose members are lifted to the enclosing namespace?
    ** VB: <HideModuleName>
    ** C#: not available
    ** F#: not available
  • How do you declare something where people have to qualify member access, unless they specifically import them all?
    ** VB: NOT POSSIBLE, BUT DESIRABLE
    ** C#: new C#/Roslyn feature, "static usings"
    ** F#: this is the default
  • How do you declare something which always requires qualification to access members?
    ** VB: not available
    ** C#: this is the default
    ** F#: [<RequiresQualification>]
There's a clear VB parity gap here, which has been asked-for many times on user-voice and connect.

Anthony's proposal is for "static classes", no more, no less. The question is (1) whether to allow it, (2) whether to call it "explicit module" or "shared class" or something.

RESOLUTION: yes, we should allow it.

Syntax

We need a new keyword or keyword pair. Here are the candidates we came up with.

Strict Module
MustQualify Module
Explicit Module
Opaque Module
MustQualifyMember Module
Closed Module
Protected Module
Private Module
Static Module
Greedy Module
Shared Module
Shared Class
Namespace Module

RESOLUTION: We'll use "strict module" as the best of a bad bunch. We're open to suggestions for better names.

Semantics

The metadata we emit for "Strict Module" should be the same as the metadata for C# static classes.

Q. Generics? We could say "strict modules" can be generic. Note: you also can't have generic modules in VB.
RESOLUTION: Strict modules CAN be generic; however generic modules CANNOT contain extension methods.

Q. Nesting? Currently VB modules can't be nested. C# static classes can be nested.
RESOLUTION: Strict modules cannot be nested.

Q. For C#/Roslyn new feature, when they import static types, will they also allow importing VB modules?
RESOLUTION: up to C#

Q. How about VB importing C# static classes?
RESOLUTION: Currently, VB can import C#'s static classes, and if they come with [StandardModule] then we auto-open. We will continue to do this. NOTE: THIS IS A BREAKING CHANGE: e.g. you will no longer be able to declare variables of type "Console". Doesn't seem a very bad breaking change.

Q. What about <HideModuleName>?
A. Intellisense will ignore this attribute on strict modules.
Marked as answer by lwischik on 4/3/2014 at 10:31 AM
Apr 15, 2014 at 1:45 AM
Like the concept, don't like the name. "Strict Module" in VB implies to me the code inside this module is Option Strict On// strictly speaking that is ;)

"Shared Class" on the other hand is what I would expect when translating "static class" from C#. It would also fit closest to the case of partial private sub new scenario.

It would also be nice if we could turn OFF all auto importing. For example, let's say Console is a standard module and gets auto-opened. In a VB project with
Imports Microsoft.VisualBasic
Imports System

You'd be faced with conflict resolution over Writeline.

It'd be nice to be able to indicate you don't want unqualified access to shared class members. (aliases is too narrow/ugly)
For VB, this would have to be surfaced as an "Option", eg Option AutoImports Off or something like that.
Apr 15, 2014 at 11:26 AM
Did you mean "Shared Class" (then I'll reread because I didn't get it)
or
Did you typo and mean "Shared Module"?
Apr 15, 2014 at 12:08 PM
Shared Class. A Module is already Shared//static
Apr 17, 2014 at 4:06 AM
Definitely agree that the Partial Private Sub workaround should be retired (creativity notwithstanding)!

I like BillMcC's Shared Class suggestion. My only question about it is based on how I've used Modules and Classes...I'll use a module when each declaration stands on its own and I don't want a constructor (even though I can add a constructor to a Module I never do - I would create a Shared Sub New in a class instead).

PROPOSAL: USE "CONTAINED MODULE"

So, for the scenario in which I want to add to a namespace but do not want the declarations lifted, I prefer something like "Contained Module" to "Strict Module." I think that using the Strict keyword conflates with the existing definition of Strict and doesn't cleanly describe what it's actually being Strict about. Conversely, "Contained" I think describes the intent more clearly - that the members are only accessible if you specify the containing module's name.
Contained Module Monkey 'basically a namespace container (scaffolding a level of the hierarchy)
   Public Function x() as Integer = Year(Now) 'shameless plug for expression as return value
End Module

Module PervasiveMonkey 'lifted for general use
   Public Function y() as Integer
      Return 20
   End Function
End Module

Module Program
   Public Sub New()
      Console.Writeline(Monkey.x)
      Console.Writeline(y)
   End Sub
End Module

IDEA: IMPLICITLY CREATE CONTAINED MODULES

(This one might be boutique exotic goldplating heresy)...to add to the Contained Module concept - another idea would be to allow adding declarations directly into a Namespace block and let VB create implicit Contained Modules (c# static classes) from the outermost namespace level.

so:
Namespace Grover.Cleveland
   Function AmIPresidential() as Boolean = True
End Namespace
Would create an implicit Contained Module Cleveland (static class) in the Grover namespace, as:
.namespace Grover
{
  .class private abstract auto ansi sealed beforefieldinit Cleveland
  {
    .method public static boolean AmIPresidential() cil managed
    {
      ...
    }
  }
}
(Adding a Cleveland Class in the Grover Namespace would be met with an error since it's already implicitly defined as a Contained Module).

PROPOSAL: ALLOW NESTING OF CONTAINED MODULES

Contained Modules (whether explicitly or implicitly defined) could accommodate nesting, which would be useful when laying out static hierarchies. The current alternative is to either create explicit Namespace blocks or rely on nesting Public Classes within Modules and play tricks to eliminate the constructors (which won't even be an option anymore).
Contained Module RootModule
   Function Here() as String = "Quip"

   Contained Module NestedModule '
      Function InHere() as String = "Quill Pen"
   End Module
End Module
(Regular Modules cannot be nested within a Contained Module)
Module Program
   Sub Main()
      Console.Writeline(RootModule.Here)
      Console.Writeline(RootModule.NestedModule.InHere)

      'also, accessing a static from a With Block would be highly convenient:
      With RootModule
         Console.Writeline(.NestedModule.InHere)
      End With
   End Sub
End Module
Sep 19, 2014 at 9:44 PM
Edited Sep 19, 2014 at 9:45 PM
Lucian/Anthony,

I read the 9/10 updates to the comment and am still perplexed as to why Explicit Modules could not be nested? This would be useful, particularly when organizing hierarchies of shared methods. Nesting classes with shared methods works fine but I think a key distinction between modules and classes are that modules cannot be instantiated. So it would then be useful to have nested modules as the better option for that use case.

Craig.
Coordinator
Sep 20, 2014 at 10:10 PM
Hey Craig,

I'm not sure to which update you refer but the general feeling in the VB LDM was that nesting should be permitted for the reasons you mention. In general the momentum was behind complete functional parity with C#'s static classes, so:
  • Nesting should be permitted.
  • Being generic should be permitted.
  • Being partial should be permitted.
All of those capabilities needn't be implemented at the same time though. The highest priority scenario identified was not spilling their members into the containing namespace.

-ADG
Sep 21, 2014 at 3:03 PM
Anthony,

Good to hear. I was referring to the original post which was updated on 9/10 with (what I thought was) your suggestion to create "Explicit Module". I matched that to the Semantics section which says:

"Q. Nesting? Currently VB modules can't be nested. C# static classes can be nested.
RESOLUTION: Strict modules cannot be nested."

So I drew the conclusion that the first cut of Explicit Modules will not allow nesting.

Craig.