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.11k stars 4.04k forks source link

C# 7 Work List of Features #2136

Closed MadsTorgersen closed 7 years ago

MadsTorgersen commented 9 years ago

(revision circa April 26 2016, clarifying edit June 14, 2016)

This is a list of features are looking at for C# 7, roughly categorized according to our current level of interest and estimated plausibility.

Please don't infer anything about the final shape of C# 7 (or future versions). The list is a tracking mechanism for work, not a description of the outcome. We have a long way to go yet!

Each bucket is in no particular order. I'll try to think of a more systematic way of cross-referencing with proposals and notes, but wanted to get the list out there.

I'll keep the list updated as we look at more feature ideas and change our minds about the relative importance.

Strong interest


See also Language Feature Status (compiler team)

alrz commented 9 years ago

Which one would Customer::Name refer to?

It should be inferred from usage, but yeah there are a lot of details to think of.

dsaf commented 9 years ago

@thomaslevesque

Which one would Customer::Name refer to?

Getter is a Func<> and setter is an Action<> so it should be possible to resolve based on context. Let's not forget however, that lambda notation is used against properties to capture their name and other information which is useful for implementing DSLs (e.g. EF Code-first maps). How would that be affected? Would reduced form be still an expression?

dsaf commented 9 years ago

@thomaslevesque

A nice thing that Java 8 does and C# doesn't, though, is allow constructors to be used as method references, e.g. items.Select(SomeClass::new). I'd like to see that in C#, but I suspect it would require some changes at the CLR level too...

Not necessarily, latest F# has worked around that somehow:

https://www.visualstudio.com/en-us/news/vs2015-vs#fsharp

Constructors as first-class functions - Class names can now be used as first-class function values, representing the constructor(s) for that class.

thomaslevesque commented 9 years ago

Not necessarily, latest F# has worked around that somehow:

It does so by implicitly creating an anonymous function that calls the constructor, so it's essentially the same as using a lambda. Still, the syntax is lighter, so it would be nice to be able to do that in C#.

JohnnyBravo75 commented 9 years ago

I want to think about another feature for the list, I think the interest is not so small.

Weak references (for events or more common approach) dotnet/roslyn#101 dotnet/roslyn#2171

HaloFour commented 9 years ago

@alrz

Similarly, I had proposed a shorthand syntax to writing those kinds of simple lambdas by using a sigil in place of the argument list (#3561). For example, .Select(customer => customer.Id) might be shortened to .Select(@.Id). A method reference syntax would be more flexible but a little more verbose in these specific cases.

alrz commented 9 years ago

another feature that would be useful is ability to use the type void in generics. It totally eliminates the need for non-generic and generic interface hierarchies. as does unit for F#.

dsaf commented 9 years ago

@alrz Your suggestions deserve to be separate tickets, would you like to create them?

alrz commented 9 years ago

@dsaf sure, when I come up with more details I will. thanks.

HaloFour commented 9 years ago

@alrz IIRC that's not really possible as void denotes a lack of type which would fundamentally alter the IL that would need to be emitted, but with generics the IL needs to be identical regardless of the generic type arguments. In any case it would involve some serious changes to the BCL. If there was a solution you'd probably see the Java version where Void is a proper type, like unit, which can be used as a generic type parameter, and you'd be required to pass null as it's value. Rx did something similar, defined System.Unit which is a struct that has no value.

h82258652 commented 9 years ago

@MadsTorgersen How about dotnet/roslyn#3371 ?

gafter commented 9 years ago

@h82258652 dotnet/roslyn#3371 is now tagged "up for grabs". We'd welcome community contributions here.

TyreeJackson commented 9 years ago

@MadsTorgersen Would you all please consider adding an f-bound/CRTP generic type constraint #4332?

Also, variadic generic parameters from #166?

TyreeJackson commented 9 years ago

@MadsTorgersen Also, would you all please consider adding Undefinable* type wrapper structs outlined in dotnet/runtime#77606?

Thanks!

Kingnaoufal commented 9 years ago

Add inline interface implementation and inline override class this is helpfull An example in java that provide a way to have a "Fluent visitor".

The idea is to chain visitors like creating expression of visitors.

The return type "Anonymous type of interface" can be an object visitor or whatever and we can reuse it to access members ex:"isNative()" or we can create an other call to another object.

In c# the member types of an anonymous object are not accessible outside the scope of the function and they can't implement an interface to be passed to another function.

public interface IColumn {
   <T extends IColumnVisitor> T accept(T columnVisitor);
};

public interface IColumnVisitor {
   void visit(NativeColumn nativeColumn);

   void visit(CalculatedColumn calculatedColumn);
}

public class INativeColumn implements IColumn {
   <T extends IColumnVisitor> T accept(T columnVisitor) {
      columnVisitor.visit(this);
      return columnVisitor;
   }
};

public class ICalculatedColumn implements IColumn {
   <T extends IColumnVisitor> T accept(T columnVisitor) {
      columnVisitor.visit(this);
      return columnVisitor;
   }
};

public final class ColumnTypeDetector {
   void boolean isNative(IColumn column) {
      return column.accept(new IColumnVisitor(){
         private boolean isNative;

         private boolean isNative() {
            return isNative;
         }

         void visit(NativeColumn nativeColumn) {
             isNative = true;
         }

         void visit(CalculatedColumn calculatedColumn) {
             isNative = false;
         }
    } ).isNative();
   }
};
svick commented 9 years ago

@Kingnaoufal I think that for your example, pattern matching (#206) would be a better choice:

bool IsNative(IColumn column)
{
    switch (column)
    {
    case NativeColumn nativeColumn:
        return true;
    case CalculatedColumn calculatedColumn:
        return false;
    }
}
Kingnaoufal commented 9 years ago

@svick I agree if i want to drop the two functions and use pattern matching, but my point is not about this part of the code, it's about reusing anonymous object to chaining them and accessing there members out of the creation scope.

In c# is not possible to do that "I create some wrapper that emit IL to emulate that" and i found it useful when i want to build system that chaining expressions and reusing them later.

I think if i'm not wrong it's an easy feature to add to the compiler and add more capability to let the user choose where and when they want to create derived instance whitout writing a new class.

Second point is to let us changing default behavior of class by create an in place override of the object, i found it useful when i want to debug a library whitout source code.

We can imagine a pletory of scenario when these language shortcuts can be useful.

Regards, Naoufal

gafter commented 9 years ago

@Kingnaoufal We do not appear to have an issue/proposal corresponding to what you're suggesting.

HaloFour commented 9 years ago

@Kingnaoufal I assume that you're referring to allowing anonymous classes implement an interface and to provide that implementation. Check out proposal dotnet/roslyn#13.

TyreeJackson commented 9 years ago

@MadsTorgersen If you would, please also consider dotnet/roslyn#4436.

Thank you.

gsscoder commented 9 years ago

@erik-kallen, about semicolon: it should be optional, like Scala. If I'm not wrong Scala use semicolon inference.

Another thing I'd like to see is the removal of "{...}" curly braces for everything, except methods or multi-line closures.

:smile:

HaloFour commented 9 years ago

@gsscoder Not sure if you're being serious. Issue 2974 (intentionally not referencing it) already brought up eliminating the requirement for braces. There was also a lengthy conversation on CodePlex about implicit semicolons. I'd love to see the argument that can demonstrate why either massive effort (and resolving syntax ambiguities) are worth it beyond <whine>but I don't like (semicolons/curly braces)<whine>.

gsscoder commented 9 years ago

@HaloFour, maybe I was superficial, but I'm serious. I'll say it better: remove "{...}" for namespace declaration and types and leave it for methods and every construct inside it.

About semicolon I think it could be "easy" avoided for a great major of cases. To me is just noise... :(

Thanks for the link, I've missed it.

HaloFour commented 9 years ago

@gsscoder

I can kind of see it for namespaces. A single namespace Foo; declaration at the beginning of the file establishing the namespace for the entire file. Then you avoid the almost compulsory first indentation. Aside from compiler directives I don't think anything else in the language is file-scoped, though. There is the potential ambiguity as to what would happen if the compiler hit a second namespace directive in the middle of the file, whether that would establish a new namespace for the remainder of the file or a nested namespace. Those concerns are probably minor and could probably be addressed by simply not permitting statement namespace directives to appear more than once within a file.

Types are probably similar. The vast majority of the time (high 90s percentile) I imagine people adhere to file-per-type conventions. I personally don't like the idea of seeing a type without braces, but that's more a question of style as that's what I'm used to (Scala still looks like APL-lite to me). I assume that you'd propose that any member appearing after the class declaration would just belong to that class?

namespace Foo;

public class Bar

public string Name { get; set; }

Something about that just doesn't feel right. But feel free to toss a proposal out there as a separate issue if you want to advocate it.

As for semicolons, considering that they couldn't entirely be removed without changing some other grammar to avoid ambiguities and that removing them in only most/some cases would just be confusing I can't imagine that the conversation is much more than a non-starter. I've always been of the opinion that if you want a different grammar there are a couple dozen other languages targeting the CLR, including Scala.

dsaf commented 9 years ago

Is there a separate issue for "Syntax for lists, Syntax for dictionaries"? I was wondering what will happen to ImmutableArray, ImmutableList, ImmutableDictionary?

dsaf commented 9 years ago

@HaloFour

...I don't think anything else in the language is file-scoped...

How about namespace imports using?

Regardless, I think this thread is becoming incomprehensible, someone above already mentioned dotnet/csharplang#2546.

gsscoder commented 9 years ago

Thanks for the link @dsaf, it's interesting.

I think that the overall philosophy has nothing todo with turning C# into F (always I hear this objection): but make the syntax terser and modernize a C# from its root.

I think it's now mature enough to lost its "coupling" from C-ish style, poorly speaking and in general.

I want spend more time thinking than typing and even more time handling data than building software structure that handle the program itself (this second part is an allusion to increment the FP constructs in C# or at least add more sugar like quoted in various proposals).

dsaf commented 9 years ago

@gsscoder I just had a very brief look at two allegedly "modern" languages - Swift and Rust. Both seem to keep using curly brackets in a lot of places and Rust insists on using semicolon. ML is about as old as C, so F# definitely doesn't have a "modern" syntax.

gsscoder commented 9 years ago

@dsaf, I was only anticipating people that see any removal from C# a walk toward making it F#.

I love F#, OCaml and in general ML-family languages, but making such thing (turning C# -> F#) makes no sense to me too.

I doesn't understand what's the problem of removing ; where it can be removed...

I don't know Swift very well, but I know that designers have embraced various functional constructs.

The same for Rust: it's a language with a lot of cool features but I think that its macro-system is too complex (it seems to me a brand new language).

Yes ML-stuff is old, but is good and leads to clean code. I've no problem programming in a language without ; and without an abuse of {...}.

But I'm agree that at some points, some syntax choice are just a matter of taste. Or maybe in past to simplify lexer/parser of the compiler...

In the end the things that really count are the ones around the type system. But I insist: I personally like terser syntaxes.

HaloFour commented 9 years ago

@gsscoder I think occasionally requiring semicolons and allowing them to be omitted elsewhere would be the definition of "unclean". It requires the developer to have to remember the idiosyncrasies of the language. Beyond that it's nothing but a preference.

HaloFour commented 9 years ago

@dsaf Oops, you're right about using directives. And you're also right about this thread being way too polluted.

gsscoder commented 9 years ago

@HaloFour, why difficult? Just use it when you want to place two expressions in the same line.

A reference on the argument.

PS: I add that I don't want C# to become like Scala that for me has too features...

paulomorgado commented 9 years ago

Don't you mean using directives, @HaloFour?

paulomorgado commented 9 years ago

Microsoft has announced that, in a future version of TSQL, terminating statements with semicolons will be mandatory.

HaloFour commented 9 years ago

@gsscoder I'm willing to bet that there are more ambiguities than that. Either way, there is no benefit to making the semicolon optional sometimes. There are liabilities, both in the work necessary to change how the language is parsed and in that every developer now has to remember when they can and can't omit those semicolons.

@paulomorgado You're right, updated my comment to be clear. IIRC, ANSI SQL requires semicolons. T-SQL is playing catch up, although many newer statements already require that the previous statement at least be semicolon terminated.

paulomorgado commented 9 years ago

@HaloFour, if you are referring to CTEs, the problem is with the with keyword and the grammar. Many times it is not possible to determine if the with keyword is the start of a CTE or if it belongs to the previous statement. Thus explicitly terminating the previous statement with a semicolon solves this.

I'm not a JavaScript/ECMAScript developer, but I wouldn't be surprised if at some point semicolons became mandatory also for JavaScript/ECMAScript.

gsscoder commented 9 years ago

@HaloFour, @paulomorgado, however the trend seems to be just that (removal of ;), excluding some minor exceptions.

paulomorgado commented 9 years ago

I'm sure you serious data to back that up, @gsscoder.

gsscoder commented 9 years ago

@paulomorgado, what does it mean?

paulomorgado commented 9 years ago

I don't see that trend at all. But I'm willing to assume that it's because I don't know what everyone is doing and thinking.

gsscoder commented 9 years ago

@paulomorgado, not everyone... But language and language designers: it's absolutely something less small to know and monitor, I think.

TyreeJackson commented 9 years ago

@gsscoder

I don't like the idea of removing curly braces, especially from classes. What if you have nested types? This seems like a fundamental change to the language.

Consider:

public class OuterClass
private class InnerClass
public void DoSomething() {}
public void DoSomethingElse() {}

How is the compiler suppose to figure out that the above is actually suppose to be:

public class OuterClass
{

    private class InnerClass
    {
        public void DoSomething() {}
    }

   public void DoSomethingElse() {}
}

Also, removing the semi-colons seems akin to removing periods from the English language. I'm all for conciseness, but this has the makings of the start of trying to mutate C# into a different language entirely.

I'm not sure that the compiler could figure out that the following:

using System;

public class Program
{
    private static Subordinate subordinate1 = null
    private static Subordinate subordinate2 = new Subordinate("Subordinate2")

    public static void Main()
    {
        var sub = Program.getSubordinate()
        Console.WriteLine(sub(null).Message)
    }

    public static Func<object, Subordinate> getSubordinate()
    {
        if (Program.check_something())
        return
        Program.get_something
        (
            0, 
            true, 
            string.Empty
        )
// WARNING!!!! Uh oh?  there is an ambiguity here.
// Are we calling the return of get_something with this 
// next line before returning?
        (Program.subordinate1??Program.subordinate2).get_something()

        return obj=>new Subordinate("New sub 2")
    }

    public static Func<object, Subordinate> get_something(int arg1, bool arg2, string arg3)
    {
        return obj=>new Subordinate("New sub")
    }

    public static bool check_something()
    {
        return new Random().Next(1, 100) < 50
    }

}
public class Subordinate
{
    public string Message { get private set }
    public Subordinate(string message) { this.Message = message }
    public Func<object, Subordinate> get_something() { return null }
}

is actually suppose to be:

using System;

public class Program
{
    private static Subordinate subordinate1 = null;
    private static Subordinate subordinate2 = new Subordinate("Subordinate2");

    public static void Main()
    {
        var sub = Program.getSubordinate();
        Console.WriteLine(sub(null).Message);
    }

    public static Func<object, Subordinate> getSubordinate()
    {
        if (Program.check_something())
        return
        Program.get_something
        (
            0, 
            true, 
            string.Empty
        );

        (Program.subordinate1??Program.subordinate2).get_something();

        return obj=>new Subordinate("New sub 2");
    }

    public static Func<object, Subordinate> get_something(int arg1, bool arg2, string arg3)
    {
        return obj=>new Subordinate("New sub");
    }

    public static bool check_something()
    {
        return new Random().Next(1, 100) < 50;
    }

}
public class Subordinate
{
    public string Message { get; private set; }
    public Subordinate(string message) { this.Message = message; }
    public Func<object, Subordinate> get_something() { return null; }
}

Note the warning comment in the version without semi colons. How does the compiler know that we are not calling the Func<object, Subordinate> returned by get_something method of the Program class with the next line where we are applying a null conditional operator to two Subordinate type fields before executing the get_something method on that object that happens to return the same Func<object, Subordinate> function type?

In other words are we expecting the equivalent of this:

return Program.get_something(0, true, string.Empty)(Program.subordinate1??Program.subordinate2).get_something();

or this:

return Program.get_something(0, true, string.Empty);

(Program.subordinate1??Program.subordinate2).get_something();

While this is a contrived case, it is still a very possible one.

Here is a dotnetfiddle of the this: https://dotnetfiddle.net/2ooBJO

dsaf commented 9 years ago

@TyreeJackson

Also, removing the semi-colons seems akin to removing periods from the English language.

My favourite argument, because it's true :).

How is the compiler suppose to figure out that the above is actually suppose to be:

I think it would mean introducing the "syntactically significant whitespace" (yeach :unamused:).

HaloFour commented 9 years ago

@gsscoder

There appears to be a trend in designing new programming languages. They're almost as bad as JavaScript frameworks; everyone has their own. Those languages often don't require semicolons (and, as @dsaf mentioned, syntactically significant whitespace) because they're based on other languages with the same design. Perhaps those languages are so awful that people just feel the need to keep reinventing them.

Can you cite an example of a programming language that went from requiring semicolons to then allowing them to be optional, without requiring syntactically significant whitespace?

gsscoder commented 9 years ago

@HaloFour, I don't think this is possible... Obviously you need to switch to significant whitespace that's a thing I love, but as always in life is a matter of taste.

Removal of ; and introduction of significant ws can let the language keep some curly brace {...} too, which I'd like to be removed only for "top-level" constructs (ns, type definitions).

For example F# use significant/ws but use {...} blocks for computation expressions (and obviously doesn't need ; at the end of each line; but can use it for join two expression in the same line).

TyreeJackson commented 9 years ago

@gsscoder The problem with significant whitespace is that it adds significant amounts of indentation.

Consider this next block of code using partial classes containing nested "top-level" constructs:

fourthLayer.cs

namespace MagicFramework
{

    using System;
    using System.Collections.Generic;
    using System.Linq;

    namespace subnamespace
    { partial class firstLevel
    { partial class secondLevel
    { partial class thirdLevel
    {

        public interface IFourthLevel
        {
            void DoSomething();
        }

        public partial class fourthLevel : IFourthLevel
        {
            public void DoSomething()
            {
                this.do_something_really_interesting_in_some_private_method
                (
                    1,
                    true,
                    String.Empty
                );
            }
        }

    }}}}
}

Now look at the significant whitespace indentation version:

namespace MagicFramework

    using System;
    using System.Collections.Generic;
    using System.Linq;

    namespace subnamespace
        partial class firstLevel
            partial class secondLevel
                partial class thirdLevel
                    public interface IFourthLevel
                        void DoSomething()

                    public partial class fourthLevel : IFourthLevel
                        public void DoSomething()
                            this.do_something_really_interesting_in_some_private_method
                            (
                                1,
                                true,
                                String.Empty
                            )

Now imagine the above with descriptive method names. It's hard enough managing horizontal whitespace usage and trying to keep things aligned left. Make the whitespace significant, and now things get really messy really fast.

In my opinion, the only significance whitespace should have is in helping to ease the task of humans reading code. That requires flexibility to determine when it is best to apply or not apply said whitespace. Once you make the whitespace compiler significant, that flexibility is lost and you now have to follow a set of rules.

In other words, I feel that, in terms of significance, curly braces are for the compiler and whitespace is for humans.

whoisj commented 9 years ago

I think it would mean introducing the "syntactically significant whitespace" (yeach :unamused:).

I initiate the Internet revolt protocol here. :scream:

Obviously you need to switch to significant whitespace that's a thing I love, but as always in life is a matter of taste.

And it tastes terrible. If I wanted Python, I'd be coding in Python. :snake:

gsscoder commented 9 years ago

@TyreeJackson, if you keep everything in one file; but if you divide sources by types (+/-) I don't see problems.

I'd use two space indentation in sample like yours and anyway I see it as readable. I repeat it's a metter of taste, I'm sick of ; and {...} super-presence. And if you read all my posts I'll keep {...} for methods and internal constructs. I would also leave ; for put two (or more) expressions in one line.

@whoisj, how code looks is far different from type system. Even with significant/ws C# will not became a language thought with a dynamic typing discipline like Python (which is a nice language full of cool libs and very cool for scientific computations).

I'm honestly surprised to see such a strong attachment to certain syntactic forms... But taste is taste and anyone has its own. I obviously respect every opinion different than mine. :smile:

whoisj commented 9 years ago

C# has a nice type system, but it is not the best one available.

I'm a C# fan due to the combination of the C-like syntax and the type system.

TyreeJackson commented 9 years ago

@gsscoder

I'm not sure what you mean by "divide sources by types". If you are referring to having only one type definition per file, my example does that. The other classes are declared in other files. This is an example of class nesting using partial classes to separate the nested types across separate files. With or without the separating of types into separate files, the proliferation of indentation will result in verifiable horizontal scrolling for some who do not have large horizontal resolutions. It will result in people needing higher resolution monitors in order to avoid scrolling. This is not a matter of opinion or style.

As far as the ; for two or more expressions in one line, I've shown you an example that you have not addressed where there is ambiguity across multiple lines if the semi colon is omitted.

Here is the relevant snippet again. Does the following without semi-colons:

return
Program.get_something
(
    0, 
    true, 
    string.Empty
)
(Program.subordinate1??Program.subordinate2).get_something()

resolve to:

return Program.get_something(0, true, string.Empty)(Program.subordinate1??Program.subordinate2).get_something();

or this:

return Program.get_something(0, true, string.Empty);

(Program.subordinate1??Program.subordinate2).get_something();

The semi-colon makes it clear to both human and compiler where one multi-line statement ends and another begins. Again, this is not simply a matter of style or opinion and is verifiable.

It all seems quantifiably too problematic.