dotnet / roslyn

The Roslyn .NET compiler provides C# and Visual Basic languages with rich code analysis APIs.
https://docs.microsoft.com/dotnet/csharp/roslyn-sdk/
MIT License
19.02k stars 4.03k forks source link

Dictionary literal (renamed from Supply default mapping class support with keyword and shorten ctor after discussion) #6965

Closed comdiv closed 7 years ago

comdiv commented 8 years ago

After discussion of this issue it was transformed to :

Syntax notion:

DICTLITERAL = { MAPPING [,MAPPING*] [,]} # braced, comma delimited mappings with possible trail comma
MAPPING = KEY : VALUE #mapping is ':' delimited pair of KEY and VALUE
VALUE = ANY_CSHARP_EXPRESSION # value can be anything
KEY = CSHARP_NAME | STRING # key is valid C# name or String

Production

KEY^ :
    KEY -> 
    CSHARP_NAME -> nameof(KEY) 
    STRING -> STRING
VALUE^ :
    VALUE -> VALUE
MAPPING^ :
    MAPPING -> [ KEY^ ] = VALUE^
DICTLITERAL^ ->
    new System.Collection.Generic.Dictionary<string,object> { MAPPING^ [,MAPPING^]* } 

So example:

var d = { x : 1 };
/*
x-> nameof(x) ->  "x"
1-> 1
x:1 -> ["x"] = 1
*/
var d = new System.Collection.Generic<string,object>{ ["x"] = 1};

':' is required for it - it allow compiler ditinguish DOCLITERAL from invalid anonymous declaration with forgoten new, no mix of:

object d = { x =1 }; // invalid anonymous 
//and
object d2 = {x:1}; // valid dictionary

It naturally support nesting

var d = {x:1, y:{z:2}}; //->
var d = new System.Collection.Generic.Dictionary<string,object>{ ["x"] = 1, ["y"] = 
    new System.Collection.Generic.Dictionary<string,object>{["z"]=2}};

And it's compatible with JavaScript objects and close to JSON - so it simplify interop tasks (for example ElasticSearch/MogoDb query construction)

///////////////////////////////////////////////////////////////////////////////////////////////// INITIAL ISSUE TO TRACE THINKING WAY:

Mappings are mostly used classes in JS/JSON/REST world and in any cases where we need to interop with them.

In .NET we have IDictionary<string,object> interface and Dictionary<string,object> class that behaves same way as JSON maps and JS object definitions and other similar things. So by the fact - it's mostly used thing.

But where are some problems - IDictionary<string,object> + Dictionary<string,object> are not in mscorlib. But it was not problem when async or dynamic or C omega support was added to C# while it's implementation based on various dependency beyond mscorlib and System, while System.Collection.Generic could be in reference path in any .net implementation - it's not real problem.

So my suggestion: while it's so widely used - make C# more comfortable with it: 1) supply type alias for it as keyword (as for int and bool) it can be map or dict

dict x = new dict { ["x"] = 1}; 

1a) it could be mapped wiselly - in declarations it will be IDictionary<string,object> and in creation it will be Dictionary<string,object> so dict x = new dict { ["x"] = 1}; should be treated as IDictionary<string,object> x = new Dictionary<string,object>{ ["x"] = 1}

dict void x (dict d) should be treated as IDictionary<stirng,object> void (IDictionary<string,object> d);

2) Make new dict optional, while left part of assigment (or argument definition) is known

dict x = { ["x"] = 1};

3) In nesting it should trat nested initializations as nested dicts:

dict x = { ["x"] = { ["y"] = 2}}; //equal to
dict x = new dict { ["x"] = new dict{["y"] = 2}};

4) Due to reasones provided with #6673, #6963, #6964 it could be very JS/JSON close and simple to use with REST interop

dict esquery = { query : { query_string : { query : "xxxx"}}};
// equal to
IDictionary<string,object> esquery = new Dictionary<string,object>{
    ["query"] = new Dictionary<string,object> {
        ["query_string"] = new Dictionary<stirng,object>{
            ["query"] = "xxxx"
        }
        }
}
//compare readability and length
HaloFour commented 8 years ago

Dictionary<,> and IDictionary<,> have been in mscorlib since 2.0 when they were introduced.

You are free to add dict as your own alias: using dict = System.Collections.Generic.Dictionary<string, object>; Adding it globally is not likely especially since it could only describe one kind of dictionary and as a keyword it would clash with a common identifier name. If anything it would be more likely to find a dictionary literal syntax where the type would be inferred.

comdiv commented 8 years ago

Man you forgot NetCore where it's planed to move from core. And if we speak about .net only you just underline that it's very common class. You comment all my issues even without any try to understand motivation and without your own egoism. If C# will be only of your requirements, i think we will not have "decimal" keyword? While you not using it and lilke doubles - all others must use System.Decimal

May be object[] syntax is bad and we must use System.Array

Dictionary<string,object> is not "one of dictionaries" - tonns of code use it AS main class to deliver dynamic data while it's ONLY RECURSIVE DICTIONARY that can nest ANY TYPE INCLUDING ITSELF.

May be you can say that where are any other Dictionary in core with such properties?

using dict = System.Collections.Generic.Dictionary<string, object>;

did you know that C# has not shared headers as C++? I don't want copy it everywhere.

If you want to criticize - the only critics should be that dict is highly used variable name so it can be very beaking change to exited code and i will agree with that. But while you want to criticize you forget this very visible fact.

did you know that ALIAS will not match my goal - read text - it should be IDictionary in all cases except of new.

Man you just became my own throll-fish and post different disagrees without filtering. If you don't have expirience with working with legacy REST from C# or with code generation of mappers from JSON specifications or deal with Angular based client from C# - why you continue spam threads that cover this thematics?

alrz commented 8 years ago

C# 7 is gonna be awesome.

HaloFour commented 8 years ago

@comdiv Having multiple behaviors depending on whether or not new is there is even worse. At least all of the existing C# aliases have the same behavior in all contexts. A dictionary literal syntax would accomplish this goal just fine.

"Legacy REST", that's a gem, really it is. How many REST services deployed on mainframes spitting out COBOL data files have you dealt with? I've dealt with more than I'd like to admit. Mapping to/from JSON or XML is a cakewalk in C#. Mapping several gigs of multi-format discriminated fixed-width with embedded dynamically-sized arrays in EBCDIC with packed decimals being vomited from a REST service, now that's fun. Some of those wire formats are government standards, too.

alrz commented 8 years ago

I was hoping to see (TKey : TValue) as a shorthand for Dictionary<TKey,TValue> but dict for IDictionary<string,object> is even better.

comdiv commented 8 years ago

While i suggest keyword for dictionaries - "dict" can be breaker (it can overlap existed variable names) better - add not keyword but "dictioanry literal" that will be compiler-time casted to Dictionary<stirng,object>.

var d = {x:1};

It's good with ':' - so we can not provide ':' as '=' alternative in all context. But it can be transformed to 'dictionary literal request' - will rename issue.

comdiv commented 8 years ago

Syntax notion:

DICTLITERAL = { MAPPING [,MAPPING*] [,]} # braced, comma delimited mappings with possible trail comma
MAPPING = KEY : VALUE #mapping is ':' delimited pair of KEY and VALUE
VALUE = ANY_CSHARP_EXPRESSION # value can be anything
KEY = CSHARP_NAME | STRING # key is valid C# name or String

Production

KEY^ :
    KEY -> 
    CSHARP_NAME -> nameof(KEY) 
    STRING -> STRING
VALUE^ :
    VALUE -> VALUE
MAPPING^ :
    MAPPING -> [ KEY^ ] = VALUE^
DICTLITERAL^ ->
    new System.Collection.Generic.Dictionary<string,object> { MAPPING^ [,MAPPING^]* } 

So example:

var d = { x : 1 };
/*
x-> nameof(x) ->  "x"
1-> 1
x:1 -> ["x"] = 1
*/
var d = new System.Collection.Generic.Dictionary<string,object>{ ["x"] = 1};
comdiv commented 8 years ago

It was some question about simialr in other threads: 1) Not <string,object> but typed <string,int> - think it's not about this literal - this is about <string,object> as most common and nestable one, for typed it think that it's not clear, the only possibility is just allow use literal AS initializer:

var d = new Dictionary<int,int>{ 1 = 2}  //but in this case it's even worse than { [1]=2}

so i think that dictionary literal is about <string,object> and nothing more

2) What about arrays - arrays in dictionary literal should be allowed and always be object[] for same reasones, but it's not so clear - while we use dictionaries for further changing and appending may be it's better to use List<object> instead of object[]

var d =  {x  : [1,2]};
((IList<object>)d["x"]).Add("x");

so may be it's better to translate [a,b..] literal as initializer for List<object> while it allow changing. Should be discussed.

3) Variables in names - suggest to use interpolation:

var i = 2;
var d = {$"x{i}" : 3 };

4) Operators for dictionaries and it's literals Not so clear as thinking about but it could be cool to get some merge semantic at C# operator level:

var d = {x:1};
var c = {y:2,a:2};
d |= c | {z:4} & {y:3} ;
// d== {x:1,y:3,a:2,z:4};

But while merging and extending is not very clear and can cause ambiguity (significantly) - think that we never will se such operations in language. While i'm using such semantics usually (extend, merge,deepmerge, setdefaults and so on) it's certainly not clear.

gafter commented 7 years ago

We are now taking language feature discussion on https://github.com/dotnet/csharplang for C# specific issues, https://github.com/dotnet/vblang for VB-specific features, and https://github.com/dotnet/csharplang for features that affect both languages.