Skip to content
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

Proposal: .net object notation format based on initializers #16648

Closed
gulshan opened this issue Jan 20, 2017 · 19 comments
Closed

Proposal: .net object notation format based on initializers #16648

gulshan opened this issue Jan 20, 2017 · 19 comments

Comments

@gulshan
Copy link

gulshan commented Jan 20, 2017

I think .net ecosystem should have it's own object notation format. The obvious question will be, why not JSON or other established formats like XML, YAML, Protobuf, Bond etc? I found one simple reason- those formats were not designed keeping .net type system in mind. Either they have their own basic types and type definition schemes. JSON is dynamically typed and was created to use with Javascript. So, when there were proposal to support JSON notation in C# by default, there were frictions. Because of the type system of those notation being different, there has to be some serialization-deserialization interfaces and mapping among the properties of an object to make them work with .net. But .net can just have it's own strongly typed human readable object notation.

As mentioned by Brice Lambson of the Entity framework team here, the basic building blocks of a .net object notation- the object, collection and index initializers are already present in C#. He has presented some examples there. Follwing is already a valid C# statement, instantiating an object-

var Project = new ConsoleApplicationProject
{
    TargetFramework = TargetFramework.Netstandard16,
    FileIncludePattern = @"**\*.cs",
    Information = new ProjectInformation
    {
        ApplicationName = "My Console App",
        VersionName = "1.0",
        Author = "Mr. Awesome"
    },
    BuildTypes = {
        [BuildType.Kind.Debug] = {
            LogEnabled = true,
            LogLevel = LogLevel.Debug
        },
        [BuildType.Kind.Release] = {
            LogEnabled = false,
            IsNative = true
        }
    },
    Dependencies = {
        new Dependency{ Name = "Microsoft.NETCore.App", Version = "1.0.0" },
        new Dependency{ Name = "Microsoft.NET.SDK", Version = "1.0.0" }
    }
};

This gist has the definitions used to construct this object. So, the .net object notation NON or sharp object notation SON can be just be using this syntax. We may omit the new keyword, as new is associated with calling the constructor and instantiating the object. But the SON is just a descriptive format, not an imperative one. Then it becomes-

ConsoleApplicationProject
{
    TargetFramework = TargetFramework.Netstandard16,
    FileIncludePattern = @"**\*.cs",
    Information = ProjectInformation
    {
        ApplicationName = "My Console App",
        VersionName = "1.0",
        Author = "Mr. Awesome"
    },
    BuildTypes = {
        [BuildType.Kind.Debug] = {
            LogEnabled = true,
            LogLevel = LogLevel.Debug
        },
        [BuildType.Kind.Release] = {
            LogEnabled = false,
            IsNative = true
        }
    },
    Dependencies = {
        Dependency{ Name = "Microsoft.NETCore.App", Version = "1.0.0" },
        Dependency{ Name = "Microsoft.NET.SDK", Version = "1.0.0" }
    }
}

There can be comments and trailing commas, as already supported by C#. As, like other formats, a SON document will be a single entity. So, probably using keyword for namespaces will not be allowed and full qualified class names are to be used when needed. Compiler aka Roslyn should provide the support for error-checking, intellisense etc for hand editing static SON documents. And standard library corefx should provide the support for reading, verifying and writing SON documents during runtime.

The .net ecosystem is big. There a lot of places where a object notation format like this can be useful like compile-time and runtime app configuration, project definition(remember the past year's JSON-XML debate?), schema definition, UI definition, data exchange among separate .net applications etc. It can even replace XAML. As a child of XML, XAML is verbose and the ambiguity between attributes and children is there. That's why people are creating simpler alternatives like http://www.ammyui.com/ . Default support for a strongly typed .net data definition may alleviate the need.

@HaloFour
Copy link

This somewhat falls under the same umbrella as #6673 and #6949.

@gulshan
Copy link
Author

gulshan commented Jan 20, 2017

A few things came to mind-

  • Only initializers and direct initialization for basic data types will be supported, constructors are not supported.
  • Other languages/ecosystems can also implement reading and translating this .net object format to their preferred format. So, a ts/js library can read and translate SON to JSON. Being dynamically typed, it will be easier for their part I guess.
  • We should look for cases, where initializers can be made less verbose. Like in the valid example I have shown, value members of BuildTypes dictionary did not need any type name. But members of Dependencies needed the type name.
  • I have found initializers were not working with read-only properties- properties without setters. But, constructors can initialize those read-only properties. Is there any reason for this? Should initializers not work with read-only properties also?

@HaloFour
Copy link

@gulshan

I have found initializers were not working with read-only properties- properties without setters. But, constructors can initialize those read-only properties. Is there any reason for this? Should initializers not work with read-only properties also?

Initializers are only short-hand for setting writable properties on the target instance. By design, you can't write to a read-only property. Even in the simple case where such a property might be initialized via the constructor there's no general way to correlate constructor parameters with said properties. In many cases the read-only property has no method to be written by a consumer, such as in the case of a calculated property.

@gulshan
Copy link
Author

gulshan commented Jan 20, 2017

With more simplified initializers, the example becomes-

ConsoleApplicationProject
{
    TargetFramework = TargetFramework.Netstandard16,
    FileIncludePattern = @"**\*.cs",
    Information = {
        ApplicationName = "My Console App",
        VersionName = "1.0",
        Author = "Mr. Awesome"
    },
    BuildTypes = {
        [BuildType.Kind.Debug] = {
            LogEnabled = true,
            LogLevel = LogLevel.Debug
        },
        [BuildType.Kind.Release] = {
            LogEnabled = false,
            IsNative = true
        }
    },
    Dependencies = {
        { Name = "Microsoft.NETCore.App", Version = "1.0.0" },
        { Name = "Microsoft.NET.SDK", Version = "1.0.0" }
    }
}

@gulshan
Copy link
Author

gulshan commented Jan 20, 2017

@HaloFour Now initializers are just shorthand for setting writable properties. And I agree it cannot become shorthand some sort of constructor. But when record types will be integrated into the language in the next version, I hope initializers will be lifted to have constructor level privilege for constructing read-only properties, instead of being just a shorthand of some sort. Else, record types will not work with this notation, as it only uses initializers.

@gulshan
Copy link
Author

gulshan commented Jan 24, 2017

If implemented, this notation should aim to replace XAML. Here is a sample XAML document converted to proposed notation-

<Canvas>
    <Rectangle StrokeThickness="30" RadiusX="97.5" RadiusY="97.5" Width="396" Height="312" >
        <Rectangle.Fill>
            <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
                <GradientStop Color="White" Offset="0"/>
                <GradientStop Color="Blue" Offset="0.5"/>
                <GradientStop Color="Black" Offset="1"/>
            </LinearGradientBrush>
        </Rectangle.Fill>
        <Rectangle.Stroke>
            <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
                <GradientStop Color="Black" Offset="0"/>
                <GradientStop Color="Red" Offset="0.5"/>
                <GradientStop Color="White" Offset="1"/>
            </LinearGradientBrush>
        </Rectangle.Stroke>
    </Rectangle>
    <TextBlock Width="172" Height="80" Canvas.Left="109" Canvas.Top="109" TextWrapping="Wrap"
        FontFamily="Baskerville Old Face" FontSize="72" Foreground="Orange">Hello
    </TextBlock>
</Canvas>
Canvas {
    Children = {
        Rectangle {
            StrokeThickness = 30, RadiusX = 97.5, RadiusY = 97.5,
            Width = 396, Height = 312,
            Fill = {
                StartPoint = (0, 0.5), EndPoint = (1, 0.5),
                GradientStops = {
                    { Color = Colors.White, Offset = 0 },
                    { Color = Colors.Blue, Offset = 0.5 },
                    { Color = Colors.Black, Offset = 1 },
                }
            },
            Stroke = {
                StartPoint = (0, 0.5), EndPoint = (1, 0.5),
                GradientStops = {
                    { Color = Colors.Black, Offset = 0 },
                    { Color = Colors.Red, Offset = 0.5 },
                    { Color = Colors.White, Offset = 1 },
                }
            },
        },
        TextBlock {
            Width = 172, Height= 80,
            Canvas.Left = 109, Canvas.Top = 109,
            TextWrapping = TextWrapping.Wrap,
            FontFamily = { FamilyName = "Baskerville Old Face" },
            FontSize = 72,
            Foreground = Brushes.Orange,
            Text = "Hello",
        }
    }
}

This sample was taken from here-
https://blogs.msdn.microsoft.com/greg_schechter/2007/05/19/wpf-silverlight-and-c-3-0-object-initializers/

@Mike-E-angelo
Copy link

Some thoughts on using Xaml, or any data-based language.

A great deal of value in Xaml is that it is designer and tooling-friendly. The same goes for XML (or JSON) but it has not been captured as effectively as it is/was in Xaml. The reason for this is that the schema was bound to a POCO and not "yet another data file" that you had to (manually or some other means) keep in sync with said data file.

The value in having designer-friendly format is that it is cheaper in the long-run to design, develop, and maintain. If you run or have ever run a consulting business, you know that finding resources that can code is much more expensive than resources that can simply manage data. Cheaper yet if that data is provided and maintained in a way that is UI-friendly. Even better/cheaper if these data files are designer-friendly and have a UI around them (less cost over the lifetime of an application).

If you are familiar with the Patterns and Practices group, they accomplished a valiant attempt at capturing the essence of this with their configuration API and app.config editor. Unfortunately it was a bit of a bear to work with (the .NET configuration API didn't help at all), but it's a good example of what the goal should ultimately be.

Additionally, XML/XAML is a language in its own (there is an "L" in each acronym after all), and is not coupled to a programming language. The idea/intent was to provide a powerful, serialized format that both VB.NET and C# developers alike could design their applications, using visual designers to really improve the experience and effeciency of the workflow.

That being said, I am (finally, it took a while 😄) resolved and open-minded towards any new efforts in replacing this paradigm. I guess to me it has always been an awesome (yet elusive) goal to create a system/solution whereby the guts were coded in C#/.NET, but then all of its configuration upon deployment was relegated to a designer-friendly format, in which devops would use a visual designer to effortlessly configure and maintain the application.

If we can somehow reach that goal, I don't care what technology you end up replacing. 👍

BTW, there is also some great, similar efforts currently underway with @ionoy's Ammy that you might want to check out:
http://www.ammyui.com/2017/01/04/ammy-modern-ui-language-for-xaml-platforms/

@gulshan
Copy link
Author

gulshan commented Jan 24, 2017

Already mentioned Ammy in the opening post, at the end. Actually, after seeing Ammy, I came up with this proposal. And POCO IS the schema here.

@Mike-E-angelo
Copy link

GAW!!! Lazy-read/scroll-to-bottom/post-my-thoughts-without-being-thorough fail! 😣

@bitbonk
Copy link

bitbonk commented Jan 24, 2017

Should there be a way to (also) create immutable object graphs using this object notation? I think it should.

@Mike-E-angelo
Copy link

@bitbonk FWIW this is not supported in System.Xaml, but it is covered and supported by Portable.Xaml. (I realize we're talking notation here, but want to ensure others of efforts already made towards such goals -- if and how they can be leveraged is another matter altogether).

@gulshan
Copy link
Author

gulshan commented Jan 26, 2017

I think there should some option for naming also like

Point origin{
    X = 0,
    Y = 0
}

or

origin : Point{
    X = 0,
    Y = 0
}

@jpierson
Copy link

jpierson commented Feb 1, 2017

Should there be a way to (also) create immutable object graphs using this object notation? I think it should.

@bitbonk, Complete with structuring and destructing and all of that type of goodness!

@gulshan
Copy link
Author

gulshan commented Feb 1, 2017

Quoting #133 (comment) by @MadsTorgersen -

It's desirable to allow the same initializers on factories as constructor calls. We need to think this through in conjunction with with expressions, and with object initializers for immutable objects.

So, there may be some probability for "object initializers for immutable objects" or Records I guess.

@gulshan
Copy link
Author

gulshan commented Feb 3, 2017

Immutable and mutable types can have separate constructs in notation. I think, {} braces and = operator for mutable types and [] brackets and ; operator for immutables like records. So, ( X : 0, Y : 0 ) means a value for immutable data type and { X = 0, Y = 0 } refers to mutable data type. The former is also the syntax for the named parameters. Does this make any sense? Or just unnecessary complexity?

@jnm2
Copy link
Contributor

jnm2 commented Feb 6, 2017

@gulshan I think just unnecessary complexity.

@gulshan
Copy link
Author

gulshan commented Feb 10, 2017

Quoting #229 (comment) by gafter

If I understand your proposal, the main point is that you could use the object initializer syntax and the compiler will recognize it as constructor parameters instead of separate property assignments. I think that is an attractive idea, and definitely something we're considering.

@birbilis
Copy link

But this is C# object notation, not .NET object notation then. And can't apply XML tools from third-party apps to parse it, but need a C# compiler?

@gulshan
Copy link
Author

gulshan commented Jun 8, 2018

Closing in favor of- dotnet/csharplang#1614

@gulshan gulshan closed this as completed Jun 8, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants