-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Constrained generics #17
Comments
Yes, I think we should do something in this area. Using the VB.NET syntax, it would be something like this:
Though I admit I'm not all that keen on the curly braces. Also, might need to consider having default groups like 'Numeric' to simplify. |
Whichever format/syntax you eventually decide on it would be great if it could also be used against variables declared as variant or object. You could make the curly brackets optional. e.g. something like
|
I prefer this syntax to the curly braces, and the semantic highlighting will help make the datatypes clearer. Still open to suggestions though.
A restricted-Variant type would impact runtime performance due to all the extra runtime type checking that would need to be done. |
In the case of restricted-Variant type, you can enforce it at compile-time without any runtime penalty by disallowing it for parameters of a Incidentally, that would dovetail nicely with the #11... |
This feels much more like BASIC than curly braces. One of the things that has always made BASIC accessible to new users is its preference for plain English over symbols with special meanings. The price for that is often more verbose code. The |
I assumed that variant/object method parameters could be mapped easily to a generic equivalent 'behind the scenes', however I'd have to admit that it might be necessary to drop constrained variants/objects just declared as variables as there doesn't seem to be a 'generic' mapping available. If I had to express a preference it would be to allow constrained variant/object as method parameters, and to accept that constrained object/variants will not be possible for ordinary variables. |
Not sure about allowing constraints to specify any of semantics (with
What can you usefully reason about By contrast, in .Net, constraints use all of semantics (i.e. This could be expressed as:
(the interfaces are hypothetical) This is much easier to reason about - Note that .Net does have a few "general" constraints (in VB.Net, |
Having good tools means that you can still shoot yourself in the foot if you want to. The original example I proposed was to select types that could be incremented by 1. This is not simple to do using either generics or interfaces. The more general point is that because VBA/VB6 is an untyped language which has strong built in hinting, the language desperately needs a more rational way of reasoning about types (in the same way as it desperately needs the ability to easily populate collection object/arrays). In fact it might even be reasonable that types themselves could have their own set of properties such as IsNumericallyIncrementable, IsConcatenatable. I see a big danger for twinbasic in becoming a basic based C#alike rather than something that has different ways of thinking and methods for doing things. Any solutions about reasoning about types should be a way of bringing such reasoning into compile time checking rather than deferring to runtime checking. |
I'm sure twinBASIC would never turn into a C#alike, because of its fundamental design imperatives (VB.Net's take in italics):
I have no doubt that these goals are immutable and non-negotiable, just as they should be IMO. I'd like to think that twinBASIC can become what Microsoft would have evolved VB6 into, had they not been distracted elsewhere. I'm hoping that means taking the best ideas from the last 20 years, allowing legacy VBx code a natural migration to current best practices. Some of those ideas might come from the .Net ecosystem, some might come from elsewhere, such as Python or FreeBasic. So long as they are implemented sympathetically and elegantly, and fit neatly into the BASIC language, I don't foresee a problem. With regard to generics, type theory is properly, intractably hard. We haven't even mentioned generic variance yet * (covariance and contravariance) , largely because it's of lesser value until class inheritance is available. But that took Microsoft until version 4 of .Net to implement, as it's really, really tough to get right. But it's impossible without a rock-solid type system to begin with, as Java's lackluster implementation proved. * Oops, I just did... |
One problem we need to recognize is that generics are for objects and those aren't objects. In .NET, everything is an object; there are no primitive data types, and there's the concept of boxing/unboxing. That simply won't exist here in twinBASIC. With a variant structure, we can potentially restrict what will be stored in the structure, so in that context it can make sense to talk about a constrained variant but that wouldn't be "generic" in the same way objects can be "generic"-ized. We need to consider whether we should require different syntax for reasoning about primitive data types vs. objects; otherwise it can lead to confusion ("hey why can't I have both |
In .Net, you can't declare constraints to be value types like this:
For the simple reason that value types (be they primitives or Structures) cannot be inherited. So only an Integer could ever satisfy the constraint. So there's no point to making it generic... I don't see why twinBASIC would work any differently, but I can see there may be some value in a
Edit: Or maybe |
While they may implement a common set of operators, they aren't the same operators and requires the compiler to emit correct instructions for different data types (e.g. floating precision math is different from integer math, currency/decimal requires special handling, dates may require additional normalization that a double doesn't do, etc.). I don't know if we can statically enforce this at compile-time without paying the runtime penalty of checking the data type, which mean it's no better than using a For internal calls where the compiler has the full visibility, it probably can infer what data type is being used as the input and thus generate correct assembly instructions. But if it's a Also, what could this mean?
The Or what about this?
Do I get a The point here is that because operators can exist on different data types, this doesn't work as a clue to whether it can be grouped and the implicit conversion rules will further confuse the matter. A set of overloaded functions would actually fulfill the requirement better:
Here, it's now more explicit and readable and we have a clearer idea of what to expect when we pass a That is not to say that I don't think constrained variants aren't potentially useful. In fact, my huge annoyance with Office's libraries is that they love using
Yes, switching may mean more verbose code but again, we want explicit & readable code. Note that we don't even need to add the boilerplate for checking the data types nor do we need to throw an error when the input is neither a |
All excellent points, and entirely correct, but I think we're talking at crossed purposes... I'm thinking initially of generic constraints, rather than As I commented earlier, I really can't see a use for So (disregarding Variant for a sec), this becomes clear: Function Increment (T As Numeric) (SomeValue As T) As T
Return SomeValue + 0.01
End Function
Sub TestIt()
Dim x As Long = 1
Dim y As Double = 1
MsgBox Increment (Of Long) (x) ' 1
MsgBox Increment (Of Double) (y) ' 1.01
' Or more succinctly, using inference:
MsgBox Increment(x) ' 1
MsgBox Increment(y) ' 1.01
End Sub The generic is typed as either
Edit: Actually, I think if you pass such a method a Function Increment(Value As Variant) As Variant
Return Value + 0.01
End Function Which would return 1.01 regardless of whether the supplied value was an integer or a floating point, because implicit coercion. If that were allowed though, a |
I've been thinking about this some more, and I think this is actually two feature requests:
I've talked about 1 above. 2 could look something like this: Dim SomeVariable As Variant Of Byte Or Integer Or Long For 2. I think As @bclothier said, it should be possible to avoid a runtime penalty for 2 when it's used internally. I would rather still allow it to be used externally though, even with the associated extra runtime cost (as long as the compiler is smart enough to know the difference and optimise accordingly). Example 1: Generic method Public Function DoSomething (Of T As IEnumerable And IDisposable) (SomeValue As T) As T
End Function Example 2: Restricted- Public Function DoSomething(SomeValue As Variant Of String Or Date) As Boolean
End Function These could even be used concurrently (not shown for brevity) |
What about a slightly different syntax? Thinking the below could be clearer than the route VB.Net took, and could also accommodate both type and Variant constraints: Non-Reference type Public Function DoSomething(Of T) (SomeValue As T) As Long _
Where T Is Value
End Function Reference type with interface Public Function DoSomething(Of T) (SomeValue As T) As Long _
Where T Is Class And IEnumerable
End Function Variant Public Function DoSomething(Of T) (SomeValue As T) As Long _
Where T Is Variant Of Integer Or Long Or Byte
End Function One other consideration - in .Net, only the name, return type and parameter types are considered when identifying candidates for overload resolution - generic constraints are not part of the equation. So methods that differ only by constraints are not allowed. I think this is because of the history of .Net - generics were only introduced in version 2. Given a clean slate as with twinBASIC, I see no reason why constraints could not be included in overload resolution - @WaynePhillipsEA do you think this is so? If constraints could be used in overload resolution, it could solve the Let / Set problem recently discussed in VB Forums: Public Class Foo(Of T)
Dim _backingField As T
Public Sub Add(item As T) Where T Is Value
_backingField = item
End Sub
Public Sub Add(item As T) Where T Is Class
Set _backingField = item
End Sub
End Class |
Btw, prior art re syntax besides VB.Net there is trait constraints in rust with dual syntax as well i.e. inline vs separate |
Another view point on this: If (per the original example) we want to increment x, then rather than constraining x As Numeric What about x supports the + operator |
Is something like the below on the roadmap for twinbasic?
The text was updated successfully, but these errors were encountered: