-
Notifications
You must be signed in to change notification settings - Fork 64
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
In Expressions #229
Comments
Thanks for addressing one of my pet niggles. My first formal programming training was in Pascal 30-odd years ago, so sets come naturally to me. And these days I spend more time in SQL than any other language. So if there's one thing I've often missed in VB, it's an 'In' construct for dealing with sets (collections in VB-speak). To the point where I once wrote an extension method to wrap 'Contains', so that I could do:
But extending that to Enum Flags(bitmasks) would be an welcome addition. Now if you could contract it a bit more, it would be extra awesome. Something like:
I think reads a little more naturally than: |
I like the idea of an This would cover, out of the box, the most obvious use case for It would also allow individual types to implement their own Question: Is |
@pricerc What does this:
mean, in current VB.NET syntax? This:
or this:
|
... had to re-read the whole thread to remember what this discussion was about - using Color.Red/Green/Blue is a bit distracting because I've also done a bit of work ORing and ANDing RGB values within Color structures... @zspitz . Re: your first comment: the basic "Item In {Set}" should indeed just be syntactic sugar over Contains. Then regarding your question re: At the time, I was thinking about the construct I had in my comment:
So, that equates to your first example:
But I think I need to flesh it out some more into a more complete example that doesn't use Red Green and Blue for bit flag names. |
Please excuse the length of this post, but I needed an example that was closer to something I've used. Given an enum (potentially fairly long) declared thus: <Flags>
Public Enum TransactionTypes
None
ArSalesOrder = 1 << 0 ' &B0000_00000000_00000001
ArDelivery = 1 << 1 ' &B0000_00000000_00000010
ArSalesInvoice = 1 << 2 ' &B0000_00000000_00000100
ArPayment = 1 << 3 ' &B0000_00000000_00001000
...
ApPurchaseOrder = 1 << 8 ' &B0000_00000001_00000000
ApPurchaseReceipt = 1 << 9 ' &B0000_00000010_00000000
ApPurchaseInvoice = 1 << 10 ' &B0000_00000100_00000000
ApPayment = 1 << 11 ' &B0000_00001000_00000000
...
End Enum You can have a transaction class and processor that looks like this: Public Class ArTransaction
Implements ITransaction
Public Shared ReadOnly ValidTransactionTypes As TransactionTypes() = {
TransactionTypes.ArSalesOrder,
TransactionTypes.ArDelivery,
TransactionTypes.ArSalesInvoice,
TransactionTypes.ArPayment
}
Dim _transactionType As TransactionTypes
Property TransactionType As TransactionTypes Implements ITransaction.TransactionType
Get
Return _transactionType
End Get
Set
' force use of one and only one type, array doesn't contain multi-value flags.
If Not ValidTransactionTypes.Contains(Value) Then
Throw New ArgumentOutOfRangeException()
End If
_transactionType = Value
End Set
End Property
Public Property TransactionData As String Implements ITransaction.TransactionData
End Class
Public Class TransactionProcessor
' process a queue of transactions, filtered by the specified range of types, supplied as a flag enum value
Public Function ProcessTransactions(transactionTypesToProcess As TransactionTypes, TransactionList As List(Of ITransaction)) As TransactionTypes
'TODO:
' validate transactionTypesToProcess vs ArTransaction.ValidTransactionTypes
Dim result As TransactionTypes = TransactionTypes.None
For Each currentTransaction As ITransaction In TransactionList
If ValidateTransactionType(transactionTypesToProcess, currentTransaction.TransactionType) Then
ProcessTransaction(currentTransaction)
result = result Or currentTransaction.TransactionType
End If
Next
Return result
End Function
Private Function ValidateTransactionType(transactionTypesToProcess As TransactionTypes, transactionType As TransactionTypes) As Boolean
Return (transactionTypesToProcess And transactionType) = transactionType
End Function
Private Sub ProcessTransaction(currentTransaction As ITransaction)
Throw New NotImplementedException()
End Sub
End Class If, however, we could have a set construct (the 'IN' operator being proposed) that works on flag enums, then a few parts (IMO) look a bit 'cleaner': Public Class ArTransaction
Implements ITransaction
Public Shared ReadOnly ValidTransactionTypes As TransactionTypes =
TransactionTypes.ArSalesOrder Or
TransactionTypes.ArDelivery Or
TransactionTypes.ArSalesInvoice Or
TransactionTypes.ArPayment
Dim _transactionType As TransactionTypes
Property TransactionType As TransactionTypes Implements ITransaction.TransactionType
Get
Return _transactionType
End Get
Set
' This will allow a multi-value flag, which is possibly not desirable.
' To force use of one and only one flag, would require counting the set bits
' in the underlying bit array and making having more than one an exception
If Not Value In ValidTransactionTypes Then
Throw New ArgumentOutOfRangeException()
End If
_transactionType = Value
End Set
End Property
Public Property TransactionData As String Implements ITransaction.TransactionData
End Class
Public Class TransactionProcessor
' process a queue of transactions, filtered by the specified range of types, supplied as a flag enum value
Public Function ProcessTransactions(transactionTypesToProcess As TransactionTypes, TransactionList As List(Of ITransaction)) As TransactionTypes
'DONE:
' validate transactionTypesToProcess vs ArTransaction.ValidTransactionTypes
If Not transactionTypesToProcess In ArTransaction.ValidTransactionTypes Then
Throw New Exception()
End If
Dim result As TransactionTypes = TransactionTypes.None
For Each currentTransaction As ITransaction In TransactionList
If currentTransaction In transactionTypesToProcess Then
ProcessTransaction(currentTransaction)
result = result Or currentTransaction.TransactionType
End If
Next
Return result
End Function
Private Sub ProcessTransaction(currentTransaction As ITransaction)
Throw New NotImplementedException()
End Sub
End Class of course the semantics of 'ValidTransactionTypes' is slightly different, which is my one concern about the concept, that I need to think through some more after a good night's sleep. |
@pricerc I'm not against extending |
WRT range expressions, as I noted here, the range expression could compile to a Range object which would have an appropriate This architecture would also allow third-parties to implement type-specific extension methods, e.g. for `Dictionary(Of TKey, TValue):
which could then be called with
which would map to:
The mapping of LINQ query syntax to method syntax works the same way, using instance methods (and preferring them over extension methods):
prints:
|
@zspitz dim ok = x In { 1, 2, 3 }
dim ok = {1, 2, 3 }.Contains( x )
dim ok = {1.2.3}.Any( Function( _x ) _x = x
dim ok = ( x = 1) Or (x=2) Or (x=3)
dim ok = (x.ComparedTo( 1 )= 0 Or x.CompartedTo( 2 )=0 Or x.ComparedTo( 3 )=0 How to choose which to use and in what situations? In the Dim dict As New Dictionary(Of String, Integer)
If "a" In dict Then ...
Dim value = dict("a") instead of Dim dict As New Dictionary(Of String, Integer)
Dim value As Integer = Nothing
If dict.TryGetValue("a", value) In dict Then ... which could be written "better" with Out Arguments Dim dict As New Dictionary(Of String, Integer)
If dict.TryGetValue("a", Out value) In dict Then ... |
You are proposing four possible rewrites:
I don't see any possible benefit in rewriting to 2; and, as you say, it introduces a new delegate object for no real benefit. Also, if we are already going to the trouble of rewriting to 3 or 4, I don't see any reason to use If I understand correctly, the benefit of 3 or 4 over 1 is that 1 has a potentially expensive array allocation, which is saved by the rewrite of 3 and 4. However, I would suggest that since the following is an array allocation in VB.NET (as well as assignment to the implicitly typed variable
then an implicit array allocation in this syntax:
isn't introducing any surprising behavior, and could thus use the simpler rewrite to
is simpler to express and understand, than
|
If I understand you correctly, you are concerned that in the specific case of checking for a key in a
would map to:
I would suggest that:
|
@ zspitz "Question: Is x Is In y preferable to x In y?" Yes, Absolutely! If x Is In y Then... If Color.Red Is In Colors Then... If (Color.Red Or Color.Blue) Is In Colors Then... |
Your third example is problematic because VB behavior will diverge from English semantics: If (Color.Red Or Color.Blue) Is In Colors Then... This would actually resolve to Purple and require that Purple be in Colors. I don't think that is what the line of code reads like in English. |
@KathleenDollard D: It is always a bit of a shock to me when I see an Or or And used that way in VB too. I can totally see people mixing that up. |
@KathleenDollard Thanx for pointing that out, but I must admit I'm not understanding why that would resolve to Purple (or any other color). Is it because of the parenthesis or because it should read; If Color.Red Is In Colors OR Color.Blue Is In Colors Then... (Which actually does make more sense) |
@WolvenRA That would work better, I think yes. So the issue is that An A good way to avoid accidentally doing this and not seeing what you did wrong, I think, is to use |
@ocdtrekkie Thanx for the explanation. I do use OrElse... for the short circuiting reason. |
this confusion around Color is exactly why I tried to describe a different scenario, since I don't think Color is really the target 'market' for this discussion, not helped by the fact that colors are things that we use that aren't actually a flags enum, which is actually what the discussion was about... so while If (Color.Red Or Color.Blue) Is In Colors Then... may not make a lot of sense. This might make more (depending on what you're trying to achieve): <Flags> Enum WidgetAttributes
None = 0
Solid = 1
Perforated = 2
Flexible = 4
Transparent = 8
Patterned = 16
...
End Enum
...
' WidgetAttributes enum name removed for clarity
'
AllowedWidgetAttributes = Solid or Transparent or Patterned or Flexible
...
Widget.Attributes = Solid or Transparent
If Widget.Attributes Is In AllowedWidgetAttributes Then... where Widget.Attributes and AllowedWidgetAttributes are both of type WidgetAttributes, and Widget.Attributes is required to be subset of AllowedWidgetAttributes. |
I'd like to discuss these in the discussion on the original issues (indicated) and discuss syntax in that context. |
In Expression
An
In
expression permits a single value (potentially from an expression) to validate if it contained in t a collection like expression.Syntax InExpr ::= (InKeyword : InKeywordSyntax)(Expr : ExpressionSyntax);
Implicit Parenthesises :-
( In (expr) )
Examples
TypeOf Multiple (#93)
With Range Expression (#25)
Enum Flags (#228)
The text was updated successfully, but these errors were encountered: