dotnet / csharplang

The official repo for the design of the C# programming language
11.09k stars 1.01k forks source link

Discussion : Omitting new keyword #350

Open vbcodec opened 7 years ago

vbcodec commented 7 years ago

There are lot situations where new keyword can be omitted.

var p = new Point();

can be

var p = Point();

Keyword new will be required to resolve ambiguity, for example if Point() function and Point class are defined and for other such cases. Benefit is saved space, particularly with calling function and passing many new objects as arguments.

Jason5Lee commented 7 years ago

My suggestion is omitting new keyword for value type.

pdelvo commented 7 years ago

This makes it a lot harder to read code because you can never be sure if you are looking at a method call or a constructor call. I don't think this is worth a few saved keystrokes.

orthoxerox commented 7 years ago

I've never liked the distinction between constructors and regular methods. It's a bit too late for this change, though.

vbcodec commented 7 years ago

@pdelvo This is valid point against. But lot of functions are called from other objects, and these will be distinguishable from classes in most cases. Additionally naming schemes for functions and classes usually are different, and this may be helpful to conclude if is called function or not. Beside of this, omitting is optional, so if some developer detect such ambiguity then may add `new' keyword to improve readability.

alrz commented 7 years ago

I believe this was proposed specifically for records as type invocation.

ufcpp commented 7 years ago

I have also seen this was proposed. For the sake of compatibility, new keyword can be omitted only for the record types (newly added syntax) in the proposal.

On the other hand, now target-typed new expression is proposed.

Thaina commented 7 years ago

I always missing value type construction syntax of C++

Point p(0,0,0);

But I would never support omitting new after assigning operator. That's so wrong and misleading and easily ambiguous

john-cd commented 7 years ago

Look into how Python uses the __call__ method on Classes.

a = A()     # executes __call__ on the class, which then calls __init__ on the instance 
a()            # executes __call__ on the instance

Callable instances are used for attributes, memoization, decoration, etc... It is a powerful pattern that is more general / less verbose than C# Attribute classes.

It may be useful to have C# callable instances in the future:

class C : ICallable
{

  public int Call()
  {
    ...
  }

  public static M()
 {
  var c  = new C();
  var d = c();  // executes Call() 
 }
} 

for things like Nullable, Lazy initialization, etc...

In Python, the confusion between class calling and instance calling is a matter of capitalization convention. In C#, this would be less clear and would suggest keeping the new keyword for object construction.

Other possible areas of confusion to think about:

(Non-)Interned strings:

string s = new String(chars);
string s2 = new StringBuilder().Append("My").Append("Test").ToString(); 

Boxed value types.

AustinBryan commented 6 years ago

I would much rather have target typed new, and do:

Point p = new;
Point p = new();
Point p = new(0, 42);
jnm2 commented 6 years ago

@AustinBryan ICYMI, that's https://github.com/dotnet/csharplang/issues/100.

whoisj commented 6 years ago

I always missing value type construction syntax of C++

Yes, well we both know this is actually the stack allocation semantics and not the "value type" semantics; but I'm just being pedantic about being semantic. 😆

bluesky2000 commented 5 years ago

Some more suggestions... new keyword could be inferred instead of being explicit

Currently Class obj = new Class();

//Suggested Class obj; Class obj(); Class obj("simple"); Class obj = null;//null, no object created

JeroMiya commented 5 years ago

How about a using statement syntax, as a complement to the static using statement, where you can explicitly bring in types from a namespace as callable like functions. That way you opt-in to the feature:

using CSharpForMarkup;
using static new Xamarin.Forms; // static new syntax, can now call all Xamarin.Form type ctors without new keyword
using static Xamarin.Forms.LayoutOptions;
using static Xamarin.Forms.StackOrientation;
using static MyApp.Localization.Strings;
using static MyApp.Resources.Styles;

// ...

        private View CreateSearchBar() =>
            StackLayout
            {
                Orientation = Horizontal,
                HorizontalOptions = FillAndExpand,
                Padding = Thickness(0, 0, 0, 20),
                Margin = Thickness(0),
                Children = {
                    // parenthesis still optional, even without new keyword, using initializer syntax
                    Entry {
                        Placeholder = PartNumberPlaceholderText,
                        HorizontalOptions = FillAndExpand,
                    }.Bind(Entry.TextProperty, nameof(ViewModel.PartNumber)),

                    Button {
                        Style = ConfirmButtonStyle,
                        Text = GoButtonText,
                    }.Bind(Button.CommandProperty, nameof(ViewModel.SearchPartHistory))
                }
            };

I would prefer to be able to import an entire namespace, as it would get cumbersome to do so one type at a time for large libraries of types, e.g. Xamarin.Forms controls.

HaloFour commented 5 years ago

@JeroMiya

Saving 12 keystrokes doesn't seem like a good benefit for making the language more difficult to read and reason about.

JeroMiya commented 5 years ago

@HalFour

I would say that I disagree with the premise of that statement. I find the resulting code easier to read and reason about, not harder. The new keyword causes syntactic clutter that is not ultimately useful and draws your attention away from the more important structures of the code.

Also C# wouldn't be the first language that lets you skip the new keyword (like Dart) or where there wasn't a syntactic distinction between constructors and other functions to begin with (Python), and it's a positive thing in those languages, in my opinion.

HaloFour commented 5 years ago

@JeroMiya

The new keyword causes syntactic clutter that is not ultimately useful and draws your attention away from the more important structures of the code.

And clearly specifies what the code does. It's not clutter, it's a necessary operator. Removing new would be like removing semicolons; a massive amount of effort that results in a net-negative of productivity.

Also C# wouldn't be the first language that lets you skip the new keyword (like Dart) or where there wasn't a syntactic distinction between constructors and other functions to begin with (Python), and it's a positive thing in those languages, in my opinion.

Those languages were designed with that in mind, it wasn't bolted on later. Not that it matters at all what other languages do. Different languages have different syntax.

JeroMiya commented 5 years ago

And clearly specifies what the code does. It's not clutter, it's a necessary operator. Removing new would be like removing semicolons; a massive amount of effort that results in a net-negative of productivity.

There's no fundamental or practical difference between a function that returns an object and a new expression that creates a new object. They're both "functions which return an object", and in this context, they're both "expressions which evaluate to an object instance". Thus, ultimately, the new keyword serves no actual purpose for the programmer in an expression tree. You could argue perhaps (I'm not an expert on the C# grammar or compiler architecture, so I wouldn't know) that the keyword serves an important (and perhaps critical) purpose for the compiler, but I disagree with the idea that the new keyword makes code easier to read or reason about.

Those languages were designed with that in mind, it wasn't bolted on later. Not that it matters at all what other languages do. Different languages have different syntax.

Dart wasn't designed with an optional new keyword from the start. It was added later, to make object trees easier to read and less cluttered. It was a very popular change.

HaloFour commented 5 years ago

@JeroMiya

They're both "functions which return an object",

No, one is a function that may return an object. The other is an allocation. It is an important distinction, behaviorally. Constructors don't return objects.

You could argue perhaps (I'm not an expert on the C# grammar or compiler architecture, so I wouldn't know) that the keyword serves an important (and perhaps critical) purpose for the compiler,

No, I argue that the keyword serves an important purpose for the reader. But it does also serve to disambiguate for the compiler as well given you could have a method and a type where the constructor has the same signature and there's no other way for the compiler to know what you intended.

JeroMiya commented 5 years ago

No, one is a function that may return an object. The other is an allocation. It is an important distinction, behaviorally. Constructors don't return objects.

To clarify, I am speaking specifically in the context of an expression or as part of a larger expression, where the distinction is mostly irrelevant. A new expression is an expression that evaluates to a value. A function call is also an expression that evaluates to a value. Both of which involve a function being called. We could get into the weeds about allocation, null guarantees, and object identity differences, but in my opinion those are more or less akin to implementation details.

But it does also serve to disambiguate for the compiler as well given you could have a method and a type where the constructor has the same signature and there's no other way for the compiler to know what you intended.

I would argue that allowing the existence of the new keyword to disambiguate between a function and a class of the same name was not a great outcome - I realize we can't change change that rule without breaking code, but it certainly isn't an argument against this feature.

And, one of the reasons I've proposed that the developer would need to opt-in using a new kind of using syntax or keyword, is specifically to address this issue. If the developer opts-in, the compiler is then free to enforce identifier ambiguity rules more consistently in this case without breaking any existing code.

It makes sense to enforce the same identifier/overload ambiguity rules as two functions named the same thing with the same arguments. For example a local function vs an imported static function with a static using statement, or two static functions from different static classes. If this happens, the programmer easily can use one of many methods to explicitly disambiguate their code: using an explicit namespace, using this.Foo(), creating a using alias (e.g. using Foo1 = ...etc...) and so on.

HaloFour commented 5 years ago

@JeroMiya

And, one of the reasons I've proposed that the developer would need to opt-in using a new kind of using syntax or keyword, is specifically to address this issue.

This would create a separate dialect of the language within the language. All because you're prefer to not see new. It doesn't make sense to expend all of this effort to solve no problems.

gulshan commented 5 years ago

In languages like F# and Dart the keyword new is optional while calling constructors. In Rust or Kotlin (languages loved by programmers according to SO), there is no new keyword. In C++, new means allocation and omitting new means object is on stack. But in C# new may not allocate for value types. I don't think making this change for saving a few keystrokes and making the code compact is feasible, because of C# language tradition. But other than that, I really don't see the new is adding any clarity to the code. I had my own proposal involving avoiding new among other things- #1614

WrongBit commented 4 years ago

Despite word 'new' is quite short, it's still A WORD. Means "something which takes my attention to read, recognize and... discover it's just creation of object". It's annoying - THAT'S WHY "c-like syntax" won over pascal or basic languages. We already have too much (key)words in a program to waste time on simplistic things. What about some 'new-symbol'? Say, '$'. Then you write:

var a = $Point(1,2);// like new Point
Line b = $(a, $(1, 4));// constructor Line(Point, Point) - we omit any types we already know

Note on last line how less chars we have to type and how clearer now it looks.

theunrepentantgeek commented 4 years ago

@WrongBit, you wrote:

Despite word 'new' is quite short, it's still A WORD. Means "something which takes my attention to read, recognize and... discover it's just creation of object".

Replacing new with $ would still result in "something that takes your attention to read, recognize and ... discover it's just creation of object". Punctuation isn't magically shorter and easier to read/comprehend than a short word.

It's worth doing a bit of research into the way that reading actually works at a physiological and cognitive level.

As a readers eye saccades across a line of text, the eye picks up quite a wide swath of text. For native English speakers, folks with very high reading speeds (say, 800-1200 wpm) will actually only glance at two or three points on each line of text. Those with more common reading speeds (say, 400-800 wpm) are likely to primarily look at longer words of text, relying on peripheral vision to pick up on shorter words. Looking individually at every word or symbol in turn is a regrettably common habit that limits people to reading in the 100-250 wpm range.

The research I've read indicates that the effective difference in reading speed (including comprehension) of a sigil (say, "$") verses a short word ("new") is effectively zero.

Worse, introducing a $ as a synonym for new (because, of course, removing new would be a breaking change) would slow down comprehension because each developer would need to learn, recognize, and understand that "it's just like new, but shorter and more difficult to type".

JeroMiya commented 4 years ago

Eliminating the new keyword requirement everywhere is a non-starter, as it potentially breaks existing code.

Introducing a new sigil to optionally replace new is also a non-starter, for reasons @theunrepentantgeek states - it's not an improvement in practice and more than likely worse.

The version of this proposal that has potential, in my view, would be something akin to a using static directive, but for constructors. Like the using static statement, such a directive lets the user opt-in. Hopefully you could select a namespace or wildcard so you don't have to put twenty of these using statements at the top of the file.

One of the primary users of this feature would be people using MVU/Redux patterns and frameworks - i.e. immutable types and declarative data structures in C#. What I have to do today to work around this is to build up a library of public static functions which are just wrappers around constructors - then, I can use a using static directive to eliminate the cruft. But it's a lot of extra effort and maintenance:

// Static Utility library:
using ThirdParty.UIFramework;
namespace UI.Utils {
  public static class UIConstructorWrappers {
    public static Button Button(
      String text = null,
      Action click = null) => new Button(text, click);

    // etc...
    // since I'm not the "owner" of the UI framework in this example,
    // I have to build up my own library to wrap every type in the UI framework.
    }
}

// My View Code:
using static UI.Strings;
using static UI.Utils.UIConstructorWrappers;

// What I'd like to do:
// Eliminate most of the library code above (unless the utility actually
// does something useful besides wrap a constructor), and replace with this:
// using new ThirdParty.UIFramework;

public static class MainPage {
  public static View Render(MainPageState state, Dispatch dispatch) =>
    StackLayout(
      Button(text: Button1Label, click: dispatch.Action1),
      Button(text: Button2Label, click: dispatch.Action2),
      StackLayout(
        orientation: Horizontal,
        children: ViewList(
          Entry(
            placeholder: EntryPlaceholder,
            text: state.EntryValue,
            textChanged: dispatch.TextChangedAction
          )
        )
      ) // etc...
    );
    // etc...
}
surfsky commented 4 years ago

look at flutter code, Drop new keyword, It's awesome and much more clear:

class MyApp extends StatelessWidget {
  Widget build(BuildContext context) {
    final wordPair = new WordPair.random();
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(title: Text('Welcome to Flutter')),
        body: Center(child: Text(wordPair.asPascalCase)),
      ),
    );
  }
}
Thaina commented 4 years ago

@surfsky No it's seriously not clear that Scaffold is class or function

gulshan commented 4 years ago

In Kotlin, method names start with lowerCases, Class names with UpperCases. That's how methods and constructors are differentiated. May be it is same in Dart. In C#, methods and other class members starts with UpperCase. So, it may seem confusing.

surfsky commented 4 years ago

It's not a big problem:

Drop-new-keyword is useful when creating a big-json-like object.

Thaina commented 4 years ago

@surfsky No, it is a problem

With your reasoning, we need to read and understand that token before we know that it is the object and it actually create new object, or it is a function and it just return value. With the current approach we don't need to know at the first glance even what the word is, if it has parentheses without new, then it is function, if it is new XXXXX( then it is a constructor. Even if I blur my vision I still see the new separated from XXXXX even without parentheses, it is constructor here and we are create new object

If there is anything that was json-like, I would really be skeptic that it should be created from JSON. It should always be created from json and always have single entrypoint, which is parsed from json data or string, Upholding the MVC or MVVM

JeroMiya commented 4 years ago

@Thaina I like to think of constructors as a kind of subset of functions which always return a new value, where other functions may return a cached value or no value at all. Technically they are semantically different but in practice I wouldn't say the distinction is very useful. Even if it were, syntax highlighting in an editor would help distinguish the two.

That being said, allowing constructors to be called using regular function syntax is a breaking change that requires per-file opt-in.

Thaina commented 4 years ago

@JeroMiya

I like to think of constructors as a kind of subset of functions

I kind of understand and think so. But then again, having new keyword is the key, it change perspective that from this point on it will allocate memory, not just call that function

Actually I familiar with this kind of concept from javascript, in that universe the class itself is function and derivation came from prototyping. So any function could be used to be newing

But that's the point. new keyword is important. It was used to distinguished between calling normal function and creating new instance of object. So omitting it only lead to confusion

JeroMiya commented 4 years ago

@Thaina The developers requesting this feature do so because it streamlines a common set of use cases - mostly declarative code with large object trees. Mostly these object trees are immutable or at least the tree itself is immutable if not each node. Probably the most common will be code-based UI views in an MVU or Redux pattern. Also common will be code-over-config frameworks, such as those to setup build systems, cloud configuration scripting, machine learning data transformation pipelines, and other similar applications.

I would argue that most of these primary use cases are ones in which it is not a high priority for the developer to know whether a given expression in the tree will result in a new value being allocated on the heap or not. Thus, although you are correct in pointing out that calling constructors using function call syntax obscures the fact that a new value is being allocated on the heap when the expression is executed, I would argue that for the most common use cases, that information is not relevant. At the end the of the expression evaluation, the result is the same: an object tree.

Further, given that this feature MUST be opt-in (I would suggest on a per-file level, enabled with a new using syntax), a developer must choose to enable this streamlined new syntax on a per-file and per-namespace level. I think you can safely say the developer is not confused about the choices they make themselves.

CyrusNajmabadi commented 4 years ago

Couldn't you just define methods taht then return the new (or cached) value you want? i.e.:

Scaffold Scaffold(...)
    => new Scaffold(...);

Now you can just call Scaffold(...) in your code without new.

Grauenwolf commented 4 years ago

I would be far more interested in something that supports in line XML or in line Json. That's probably the future I miss the most from VB.

Maybe our own object literal syntax would be more appropriate. But just dropping the new keyword seems like a halfhearted effort that won't satisfy anyone.

JeroMiya commented 4 years ago

Couldn't you just define methods taht then return the new (or cached) value you want?

Yes that is the current workaround. Internally, I have a C# code generator and command line tool that takes an assembly and some filtering options and generates a static class with constructor wrappers over every type in the assembly that matches the filtering options. It has all the drawbacks you'd expect from a code-gen tool, however (e.g. maintenance costs, forward compatibility, code bloat, etc...). And I'd argue the need for a code generator to overcome something in the language is maybe a good sign that some streamlined syntax is needed in the language.

CyrusNajmabadi commented 4 years ago

And I'd argue the need for a code generator to overcome something in the language is maybe a good sign that some streamlined syntax is needed in the language.

I honestly disagree, and often feel the opposite. If the problem can easily be solved by a generator, why bloat the language? Indeed, that's the direction we're pushing further toward with SourceGenerators.

JeroMiya commented 4 years ago

I honestly disagree, and often feel the opposite. If the problem can easily be solved by a generator, why bloat the language? Indeed, that's the direction we're pushing further toward with SourceGenerators.

Many language proposals are intended to be quality of life improvements and have workarounds. The existance of a workaround isn't disqualifying, and having to write a source code generator is a pretty extreme workaround, to be honest. I think most people wouldn't bother.

CyrusNajmabadi commented 4 years ago

I think most people wouldn't bother.

Agreed. I don't expect most people to do this. I expect some people to do this and then provide libraries that "most people" can then reference to get these capabilities.

JeroMiya commented 4 years ago

Agreed. I don't expect most people to do this. I expect some people to do this and then provide libraries that "most people" can then reference to get these capabilities.

As I said, this is not a great workaround for several reasons beyond how easily the code generation integrates into the build system (or the compiler in the case of C# source generators). Using this workaround requires careful management of using statements to avoid name collision with the wrapped types (I use an "N" prefix in the generated static methods, but it's ugly). Additionally, if the runtime JIT or AOT is unable to inline these wrappers, they result in additional function call overhead over plain new expressions. They are also code bloat, which is important on mobile and other constrained platforms. They also introduce a large number of new symbols in the assembly metadata. Depending on the size of the target assembly this can cause a slowdown in the C# editor and the build.

Additionally, one important limitation of using a static method generator, particularly relevant in C# 9, is that you are unable to use an initializer. So, for example this workaround does not work for most collections, and in C# 9, init-only properties cannot be initialized. If there is a property which is not initialized by a constructor, then properly initializing an object is impossible without a helper method.

CyrusNajmabadi commented 4 years ago

Using this workaround requires careful management of using statements to avoid name collision with the wrapped types

I don't know what that means. Why would there be name collisions here, but not have name collisions if we implicitly allow you to drop the new?

Additionally, if the runtime JIT or AOT is unable to inline these wrappers

Why would tehy be unable to inline the wrapper? It would literally just be a forwarding function.

They are also code bloat, which is important on mobile and other constrained platforms.

I don't see why tooling cannot address that.

They also introduce a large number of new symbols in the assembly metadata. Depending on the size of the target assembly this can cause a slowdown in the C# editor

This should not cause a slowdown in the C# editor. We test out perf to hundreds of thousands of symbols without issue.

Additionally, one important limitation of using a static method generator, particularly relevant in C# 9, is that you are unable to use an initializer.

Why not? We're designing an entire feature around that concept. And it's more broadly applicable than just dropping 'new'. :)

JeroMiya commented 4 years ago

I don't know what that means. Why would there be name collisions here, but not have name collisions if we implicitly allow you to drop the new?

If you generate wrappers for constructors, you're introducing a new static method per constructor. With a new syntax, you would not need to generate a wrapper, so no additional symbols are generated.

Why would tehy be unable to inline the wrapper? It would literally just be a forwarding function.

There are a number of reasons a JIT or AOT engine would choose not to inline a function regardless of its size including various thresholds.

I don't see why tooling cannot address that.

I don't see why a simple opt-in syntax improvement (already adopted by multiple languages outside of C#) that would eliminate the need for third party code generators, additional code shaking, more aggressive inlining, and which works well with object initializers, is not preferable to the status quo. Aside, perhaps, from a general resistance to any and all language proposals regardless of merit? I'm not saying it's as high priority as data classes and init-only properties in C# 9, but it certainly would be a welcome improvement in the same vein as the pattern matching improvements in C# 9 is all I'm saying.

Why not? We're designing an entire feature around that concept. And it's more broadly applicable than just dropping 'new'. :)

You cannot use the C# object initializer syntax on values returned from a method call and I'm not aware of any language proposals that allow this. Further, I would not support such a feature for init-only properties as it would make init-only properties mutable at call-stack boundaries.

HaloFour commented 4 years ago

There are a number of reasons a JIT or AOT engine would choose not to inline a function regardless of its size including various thresholds.

A static method that does nothing but call another function or constructor is right at the top of the list of functions that can be inlined.

I don't see why a simple opt-in syntax improvement

Making anything "opt-in" creates language dialects, and anything that creates a dialect needs to be a massive improvement to the language.

You cannot use the C# object initializer syntax on values returned from a method call and I'm not aware of any language proposals that allow this.

https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-04-20.md

Joe4evr commented 4 years ago

You cannot use the C# object initializer syntax on values returned from a method call and I'm not aware of any language proposals that allow this.

https://youtu.be/fIXujTOA6BE?t=2973

theunrepentantgeek commented 4 years ago

I don't see why a simple opt-in syntax improvement (already adopted by multiple languages outside of C#)

Other languages have a different threshold for the introduction of different, subtly incompatible, dialects. The C++ world, for example, seems to positively thrive on the idea that the syntax and behaviour of the language can change markedly depending on compiler flags. #halfjoking

Python 3 was introduced as a breaking change in 2008 and so far it has taken that ecosystem 12 extremely painful years to migrate. Even though we have now seen the the last release of Python 2, the pain isn't over.

The C# team has an extremely high threshold for introduction of language dialects - they have to provide extremely high amounts of value. I think that nullable reference types is the first, and quite possibly will be the last, given that NRT seeks to tackle the so called billion dollar problem.

Remember as well that each additional flag doubles the number of dialects of the language - with just a dozen flags for "simple" features like this, you end up with over four thousand dialects of C#.

Aside, perhaps, from a general resistance to any and all language proposals regardless of merit?

I don't think this is the case. The folks who hang out and response to proposals in this repo are doing so because they're passionately interested in advancing the C# language. They tend to be smart, articulate, passionate - and they seldom all agree.

For any proposal to progress, it has to have significant value - every proposal starts at -100 points.

As you'll see if you review the (long!) discussion above, there are major issues with removing the new keyword.

If you have a different solution to the problem, please suggest it. If your idea is good enough to be championed by one of the LDM, then it might even happen.

HaloFour commented 4 years ago

I could see some variation of the functor proposal supporting this request (#95). Trying to use the type name as a function would result in a call to a static Invoke method, which could wrap the call to an actual constructor:

public class Foo {
    public Foo(string value) { }

    public static Foo Invoke(string value) => new Foo(value);
}

// you write:

var foo = Foo("Value");

// compiler translates into

var foo = Foo.Invoke("Value");

This is exactly how Scala works:

val some = Some("Value")
// is actually
val some = Some.apply("Value")
JeroMiya commented 4 years ago

@theunrepentantgeek I understand the drawbacks, and I'm fully aware that this proposal is a long shot for the reasons you list. I'm just giving my feedback as someone who is not a compiler or language design expert - just someone who uses and loves C# day to day. Wearing that hat, I just wanted to express that this feature proposal would be a welcome improvement in terms of ergonomics and code readability for the kind of code I often write. I think it deserves "points" for that, at least.

Having experience with this particular feature in other languages, I can say that I really miss not having this in C#. I think it's important and useful enough to consider as a quality of life improvement, especially as declarative expression trees are becoming more mainstream in the dotnet community (e.g. MAUI MVU, Fabulous).

I see it as a feature that improves the overall ergonomics and readability of large C# expression trees, such as MVU or other code-based view functions. The new keyword in this context is unnecessary noise in an already noisy syntactic environment of C# expression trees. In general, I see C# as suffering in the general area of noise and readability in this kind of code that often encourages C# developers to seek other languages that don't suffer as much from these drawbacks (like F#, Dart). That's why I support feature proposals like this that make the language better for this use case.

Also, would it be considered less of a "dialect" if object initializers were dropped from the proposal? As in, the new using directive would be the semantic equivalent of generating static methods for each constructor with the same name as the class, but otherwise using all the same syntax. That wouldn't be quite as useful as the original proposal but would still be an improvement.

So for example, this code already works in today's C#, and it does not result in an ambiguous reference error because static methods and type names have different lookup rules:

// Foo.cs
namespace Foo
{
    public class FooClass { }
    public class FooClass2 { public FooClass2(int arg) {} }

    // plus maybe 20-30 other classes in this namespace 
    public static class FooClassStatic {
        public static FooClass FooClass() => new FooClass();
        public static FooClass FooClass2(int arg) => new FooClass2(arg);
        // plus maybe 20-30 other static methods in this static class
    }
}

// Program.cs
using Foo;
using static Foo.FooClassStatic;

var fooList = new List<FooClass> {
    FooClass(),
    FooClass(),
    FooClass2(12)
};

The new using statement would be equivalent to the above, but skips the need to write or generate that static method:

namespace Foo
{
    public class FooClass { }
    public class FooClass2 { public FooClass2(int arg) {} }
    // plus maybe 20-30 other classes in this namespace 
}

namespace Foo2
{
    public class Foo2Class {}
    public class NotGenerated {}
}

using Foo;
// "generate" static methods for all public types in Foo with public constructors
using static new Foo; 
// only "generate" a static method for a specific type
using static new Foo2.Foo2Class;

// later in a function body:
var fooList = new List<FooClass> { FooClass(), FooClass(), FooClass(), Foo2Class() };

// ERROR because we didn't import the entire Foo2 namespace, just Foo2Class.
// var x = NotGenerated();

That seems like less of a dialect of C# and more like a compiler directive to "generate" static methods of an anonymous static class that is only in scope for that .cs file (even if in reality the compiler ends up just calling the constructor directly instead of invoking an actual generated static method, unless the generated method is used as a delegate/action/etc..).

ChayimFriedman2 commented 4 years ago

I think that omitting the new in Python was great choice, but because it's dynamically typed. You can pass a function or a class (aka functor) to a function which will call them, which would not be possible with new. But C# is statically typed and I am not seeing any benefits. As stated above, it's just less clear, especially because as opposed to Python, for example, there is a difference between classes and functions.

dotfelixb commented 4 years ago

But C# is statically typed and I am not seeing any benefits. As stated above, it's just less clear, especially because as opposed to Python, for example, there is a difference between classes and functions.

Dart is statically typed, yet you can remove the new key word. Just saying 🤷🏽‍♂️

ChayimFriedman2 commented 4 years ago

But C# is statically typed and I am not seeing any benefits. As stated above, it's just less clear, especially because as opposed to Python, for example, there is a difference between classes and functions.

Dart is statically typed, yet you can remove the new key word. Just saying 🤷🏽‍♂️

Well, I don't agree with this design choice. And anyway C# made its decision since 2001, I think changing it (or even adding an option) will be confusing for no reason.

agrapine commented 3 years ago

@surfsky No it's seriously not clear that Scaffold is class or function

Not sure how that makes any difference in the result or inference of intent?