dotnet / vblang

The home for design of the Visual Basic .NET programming language and runtime library.
290 stars 66 forks source link

Typed Dictionary #235

Open ghost opened 6 years ago

ghost commented 6 years ago

I hope VB.NET can add this new syntax:

Dictionary CityTemperaturesDic (Of String, Integer)
     NewYork = 12
     London = 10
     Cairo = 15
End Dictionary

The compiler can use this to create a calss CityTemperaturesDic like this:

Public Class CityTemperaturesDic
    Inherits Dictionary(Of String, Integer)

    Public Sub New()
        Me("NewYork") = 12
        Me("London") = 10
        Me("Cairo") = 15
    End Sub

    Public Property NewYork() As Integer
        Get
            Return Me("NewYork")
        End Get
        Set
            Me("NewYork") = Value
        End Set
    End Property

    Public Property London() As Integer
        Get
            Return Me("London")
        End Get
        Set
            Me("London") = Value
        End Set
    End Property

    Public Property Cairo() As Integer
        Get
            Return Me("Cairo")
        End Get
        Set
            Me("Cairo") = Value
        End Set
    End Property
End Class

Now you can use this Typed Dictionary anywhere in you project or even expose it outside the project:

Dim Cities = new CityTemperaturesDic( )
Cities.NewYork = 6
If Cities.Cairo < 10 Then Console.WriteLine("Too Cold")

This will prevent misspelled key names, which lead to unexpected results. For example, if you use this code:

Cities["Cair"] = 5
If Cities["Cairo"] < 10 Then Console.WriteLine("Too Cold")

In design time, VB.NET can't detect the misspelled Key "Cair", and in runtime, the "Cair" key will be added to the Dictionary with value 5 without raising any exception, so the condition will always be false, because it deals with another key! This Can't happen in Typed dictionary, because a syntax error will happen in then line: Cities.Cair = 5 And Intellisense with prevent that sort of mistakes.

Note: we can make this part

Dictionary CityTemperaturesDic(Of String, Integer)

shorter, if we agreed that the keys will always be strings. I suggest:

Dictionary CityTemperaturesDic (Of Integer)

bandleader commented 6 years ago

@MohammadHamdyGhanem If the strong dictionary is being compiled at compile-time,

  1. Why does it have to use a Dictionary behind the scenes? Why can't it simply compile to:
    Public Class CityTemperaturesDic
     Public NewYork = 12
     Public London = 10
     Public Cairo = 15
    End Class
  2. Why can't you just write the class as above in the first place -- why do you need this feature at all? (aside from enforcing the Integer type of the values)
bandleader commented 6 years ago

Oh! Based on this issue as well as #234, I think I figured out what @MohammadHamdyGhanem wants.

We can already define a sealed list of values using a simple class:

Public Class CityTemperaturesDict
     Public NewYork As Integer = 12
     Public London As Integer = 10
     Public Cairo As Integer = 15
End Class

...but then new members can't be added at run-time.

To solve this problem, we can instead use a Dictionary, and add its members at run-time:

Public cityTemperaturesDict As New Dictionary
cityTemperaturesDict.Add("NewYork", 12)
cityTemperaturesDict.Add("London", 10)
cityTemperaturesDict.Add("Cairo", 5)

...BUT now we don't have Intellisense to ensure that we are typing the keys correctly. So we might attempt to access an item and misspell the key:

MsgBox(cityTemperaturesDict("Cair")) 'oops! error!
cityTemperaturesDict("Cair") = 12 'even worse -- we have not updated the desired item, and we've created a new item!

SO... what @MohammadHamdyGhanem is suggesting is something in between the two approaches above: a class that

It's a hybrid between an enum/anonymous type and a preinitialized dictionary:

Public cityTemperaturesDict As New Dictionary With {!NewYork = 12, !London = 10, !Cairo = 5}
cityTemperaturesDict.Cairo = 9 'Works, and I get full Intellisense.
cityTemperaturesDict.Cair = 9 'Compiler warns me that there is no such property.
cityTemperaturesDict.Add("Boston", 13) 'Adding an entry using the regular Dictionary methods.
cityTemperaturesDict("Boston") = 13 'Setting an entry using the regular Dictionary methods.

Now that we understand the suggestion, does it have value?

@MohammadHamdyGhanem If this is correct, kindly demonstrate to the VB team the practical scenarios in which this has value. The only one I could think of is a logging class of mine where I add metadata to the log entry, and some are standard for every log entry (request path, time/date, etc.), but I often want to add custom metadata (account being logged into, etc.). However, this can be worked around by using regular properties for the standard ones, and a Dictionary for the custom ones, as I will detail below.

How we can achieve this in VB today

Public wellKnownCities As New With {.NewYork = 12, .London = 10, .Cairo = 5}
Public customCities As New Dictionary(Of String, Integer)
wellKnownCities.Cairo = 9 'Works, and I get full Intellisense.
wellKnownCities.Cair = 9 'Compiler warns me that there is no such property.
customCities.Add("Boston", 13) 'Adding an entry using the regular Dictionary methods.
customCities("Boston") = 13 'Setting an entry using the regular Dictionary methods.

...OR via the custom CityTemperaturesDic class example in the opening comment.

reduckted commented 6 years ago

Now that we understand the suggestion, does it have value?

I doubt it. It doesn't seem like a common use case (though feel free to prove me wrong), and as shown in the opening comment, you can already do this manually by defining a class that inherits from Dictionary(Of String, Integer) without any real difficulty.

ghost commented 6 years ago

@bandleader: I wouldn't add any more keys like this in design-time, instead any new key I discover I need, I will go back and add it to the definition of the Typed Dictionary, and the compiler will regenerate the new members for it in the hidden class. But I can still add keys at run time, based on user entries or some system events... etc. So. the Typed Dictionary will serve as: 1- a dictionary with predefind keys without having to write a lot of constants and static readonly fields to hold the names of the keys in well-written code. 2- some sort of Enum that can have non-numeric values 3- a Tuple that can be extended in runtime. 4- a hard-typed ExpandoObject which is also a dictionay but typed dictionary has strong types not all objects. In fact my suggession can be used to improve the implemntation of the ExpandoObject in cases we define its members not getting them from an external API. 5- a record class that can be extended in runtime. The Typed Dictionary is a semi-static semi-dynamic type.

ghost commented 6 years ago

By the way, I have another suggestion for VB to give us the ability to define our code blocks and tell the compiler how to generate classes for then. This is not a designer code generation. This is a syntax that converted to a generated classes in runtime. If this happens, we call expand the language beyong our imagination, and share Code blocks definitions say as NuGets. I suggest the user-defined syntax stasts with some sympole like @:

@Dictionary CityTemperaturesDic
     NewYork = 12
     London = 10
     Cairo = 15
End Dictionary
@Flag Color
  red
  Green
  Blue
End Flag

etc

KathleenDollard commented 6 years ago

@MohammadHamdyGhanem

When you use the phrase "code blocks," do you have the Clipper S87+ code blocks in mind?