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

C# Design Notes for Jan 21, 2015 #98

Closed
MadsTorgersen opened this Issue Jan 27, 2015 · 249 comments

Comments

@MadsTorgersen
Contributor

MadsTorgersen commented Jan 27, 2015

C# Design Meeting Notes for Jan 21, 2015

Quotes of the day:

Live broadcast of design meetings: we could call it C#-SPAN

We've made it three hours without slippery slopes coming up!

Agenda

This is the first design meeting for the version of C# coming after C# 6. We shall colloquially refer to it as C# 7. The meeting focused on setting the stage for the design process and homing in on major themes and features.

  1. Design process
  2. Themes
  3. Features

See also Language features currently under consideration by the language design group.
#1. Design process

We have had great success sharing design notes publicly on CodePlex for the last year of C# 6 design. The ability of the community to see and respond to our thinking in real time has been much appreciated.

This time we want to increase the openness further:

  • we involve the community from the beginning of the design cycle (as per these notes!)
  • in addition to design notes (now issues on GitHub) we will maintain feature proposals (as checked-in Markdown documents) to reflect the current design of the feature
  • we will consider publishing recordings of the design meetings themselves, or even live streaming
  • we will consider adding non-Microsoft members to the design team.

Design team

The C# 7 design team currently consists of

Anders, as the chief language architect, has ultimate say, should that ever become necessary. Mads, as the language PM for C#, pulls together the agenda, runs the meetings and takes the notes. (Oooh, the power!)

To begin with, we meet 4 hours a week as we decide on the overall focus areas. There will not be a separate Visual Basic design meeting during this initial period, as many of the overall decisions are likely to apply to both and need to happen in concert.

Feature ideas

Anyone can put a feature idea up as an issue on GitHub. We'll keep an eye on those, and use them as input to language design.

A way to gauge interest in a feature is to put it up on UserVoice, where there's a voting system. This is important, because the set of people who hang out in our GitHub repo are not necessarily representative of our developer base at large.

Design notes

Design notes are point-in-time documents, so we will put them up as issues on GitHub. For a period of time, folks can comment on them and the reactions will feed into subsequent meetings.

Owners and proposals

If the design team decides to move on with a feature idea, we'll nominate an owner for it, typically among the design team members, who will drive the activities related to the design of that feature: gathering feedback, making progress between meetings, etc. Most importantly, the owner will be responsible for maintaining a proposal document that describes the current state of that feature, cross-linking with the design notes where it was discussed.

Since the proposals will evolve over time, they should be documents in the repo, with history tracked. When the proposal is first put up, and if there are major revisions, we will probably put up an issue too, as a place to gather comments. There can also be pull requests to the proposals.

We'll play with this process and find a balance.

Other ways of increasing openness

We are very interested in other ideas, such as publishing recordings (or even live streaming?) of the design meeting themselves, and inviting non-Microsoft luminaries, e.g., from major players in the industry, onto the design team itself. We are certainly open to have "guests" (physical or virtual) when someone has insights that we want to leverage.

However, these are things we can get to over time. We are not going to do them right out of the gate.

Decisions

It's important to note that the C# design team is still in charge of the language. This is not a democratic process. We derive immense value from comments and UserVoice votes, but in the end the governance model for C# is benevolent dictatorship. We think design in a small close-knit group where membership is long-term is the right model for ensuring that C# remains tasteful, consistent, not too big and generally not "designed by committee".

If we don't agree within the design team, that is typically a sign that there are offline activities that can lead to more insight. Usually, at the end of the day, we don't need to vote or have the Language Allfather make a final call.

Prototypes

Ideally we should prototype every feature we discuss, so as to get a good feel fro the feature and allow the best possible feedback from the community. That may note be realistic, but once we have a good candidate feature, we should try to fly it.

The cost of the prototyping is an issue. This may be feature dependent: Sometimes you want a quick throwaway prototype, sometimes it's more the first version of an actual implementation.

Could be done by a member of the design team, the product team or the community.

Agenda

It's usually up to Mads to decide what's ready to discuss. Generally, if a design team member wants something on the agenda, they get it. There's no guarantee that we end up following the plan in the meeting; the published notes will just show the agenda as a summary of what was actually discussed.
#2. Themes

If a feature is great, we'll want to add it whether it fits in a theme or not. However, it's useful to have a number of categories that we can rally around, and that can help select features that work well together.

We discussed a number of likely themes to investigate for C# 7.

Working with data

Today’s programs are connected and trade in rich, structured data: it’s what’s on the wire, it’s what apps and services produce, manipulate and consume.

Traditional object-oriented modeling is good for many things, but in many ways it deals rather poorly with this setup: it bunches functionality strongly with the data (through encapsulation), and often relies heavily on mutation of that state. It is "behavior-centric" instead of "data-centric".

Functional programming languages are often better set up for this: data is immutable (representing information, not state), and is manipulated from the outside, using a freely growable and context-dependent set of functions, rather than a fixed set of built-in virtual methods. Let’s continue being inspired by functional languages, and in particular other languages – F#, Scala, Swift – that aim to mix functional and object-oriented concepts as smoothly as possible.

Here are some possible C# features that belong under this theme:

  • pattern matching
  • tuples
  • "denotable" anonymous types
  • "records" - compact ways of describing shapes
  • working with common data structures (List/Dictionary)
  • extension members
  • slicing
  • immutability
  • structural typing/shapes?

A number of these features focus on the interplay between "kinds of types" and the ways they are used. It is worth thinking of this as a matrix, that lets you think about language support for e.g. denoting the types (type expressions), creating values of them (literals) and consuming them with matching (patterns) :

Type Denote Create Match
General T new T(), new T { x = e } T x, var x, *
Primitive int, double, bool 5, .234, false 5, .234, false
String string "Hello" "Hello"
Tuple (T1, T2) (e1, e2) (P1, P2)
Record { T1 x1, T2 x2 } new { x1 = e1, x2 = e2 } { x1 is P1, x2 is P2 }
Array T[] new T[e], { e1, e2 } { P1, P2 }, P1 :: P2
List ? ? ?
Dictionary ? ? ?
...

A lot of the matrix above is filled in with speculative syntax, just to give an idea of how it could be used.

We expect to give many of the features on the list above a lot of attention over the coming months: they have a lot of potential for synergy if they are designed together.

Performance and reliability (and interop)

C# and .NET has a heritage where it sometimes plays a bit fast and loose with both performance and reliability.

While (unlike, say, Java) it has structs and reified generics, there are still places where it is hard to get good performance. A top issue, for instance is the frequent need to copy, rather than reference. When devices are small and cloud compute cycles come with a cost, performance certainly starts to matter more than it used to.

On the reliability side, while (unlike, say, C and C++) C# is generally memory safe, there are certainly places where it is hard to control or trust exactly what is going on (e.g., destruction/finalization).

Many of these issues tend to show up in particular on the boundary to unmanaged code - i.e. when doing interop. Having coarse-grained interop isn't always an option, so the less it costs and the less risky it is to cross the boundary, the better.

Internally at Microsoft there have been research projects to investigate options here. Some of the outcomes are now ripe to feed into the design of C# itself, while others can affect the .NET Framework, result in useful Roslyn analyzers, etc.

Over the coming months we will take several of these problems and ideas and see if we can find great ways of putting them in the hands of C# developers.

Componentization

The once set-in-stone issue of how .NET programs are factored and combined is now under rapid evolution.

With generalized extension members as an exception, most work here may not fall in the language scope, but is more tooling-oriented:

  • generating reference assemblies
  • static linking instead of IL merge
  • determinism
  • NuGet support
  • versioning and adaptive light-up

This is a theme that shouldn't be driven primarily from the languages, but we should be open to support at the language level.

Distribution

There may be interesting things we can do specifically to help with the distributed nature of modern computing.

  • Async sequences: We introduced single-value asynchrony in C# 5, but do not yet have a satisfactory approach to asynchronous sequences or streams
  • Serialization: we may no longer be into directly providing built-in serialization, but we need to make sure we make it reasonable to custom-serialize data - even when it's immutable, and without requiring costly reflection.

Also, await in catch and finally probably didn't make it into VB 14. We should add those the next time around.

Metaprogramming

Metaprogramming has been around as a theme on the radar for a long time, and arguably Roslyn is a big metaprogramming project aimed at writing programs about programs. However, at the language level we continue not to have a particularly good handle on metaprogramming.

Extention methods and partial classes both feel like features that could grow into allowing generated parts of source code to merge smoothly with hand-written parts. But if generated parts are themselves the result of language syntax - e.g. attributes in source code, then things quickly get messy from a tooling perspective. A keystroke in file A may cause different code to be generated into file B by some custom program, which in turn may change the meaning of A. Not a feedback loop we're eager to have to handle in real time at 20 ms keystroke speed!

Oftentimes the eagerness to generate source comes from it being too hard to express your concept beautifully as a library or an abstraction. Increasing the power of abstraction mechanisms in the language itself, or just the syntax for applying them, might remove a lot of the motivation for generated boilerplate code.

Features that may reduce the need for boilerplate and codegen:

  • Virtual extension methods/default interface implementations
  • Improvements to generic constraints, e.g.:
    • generic constructor constraints
    • delegate and enum constraints
    • operators or object shapes as constraints (or interfaces), e.g. similar to C++ concepts
  • mixins or traits
  • delegation

Null

With null-conditional operators such as x?.y C# 6 starts down a path of more null-tolerant operations. You could certainly imagine taking that further to allow e.g. awaiting or foreach'ing null, etc.

On top of that, there's a long-standing request for non-nullable reference types, where the type system helps you ensure that a value can't be null, and therefore is safe to access.

Importantly such a feature might go along well with proper safe nullable reference types, where you simply cannot access the members until you've checked for null. This would go great with pattern matching!

Of course that'd be a lot of new expressiveness, and we'd have to reconcile a lot of things to keep it compatible. In his blog, Eric Lippert mentions a number of reasons why non-nullable reference types would be next to impossible to fully guarantee. To be fully supported, they would also have to be known to the runtime; they couldn't just be handled by the compiler.

Of course we could try to settle for a less ambitious approach. Finding the right balance here is crucial.

Themeless in Seattle

Type providers: This is a whole different kind of language feature, currently known only from F#. We wouldn't be able to just grab F#'s model though; there'd be a whole lot of design work to get this one right!

Better better betterness: In C# we made some simplifications and generalizations to overload resolution, affectionately known as "better betterness". We could think of more ways to improve overload resolution; e.g. tie breaking on staticness or whether constraints match, instead of giving compiler errors when other candidates would work.

Scripting: The scripting dialect of C# includes features not currently allowed in C# "proper": statements and member declarations at the top level. We could consider adopting some of them.

params IEnumerable.

Binary literals and digit separators.
#3. Features

The Matrix above represents a feature set that's strongly connected, and should probably be talked about together: we can add kinds of types (e.g. tuples, records), we can add syntax for representing those types or creating instances of them, and we can add ways to match them as part of a greater pattern matching scheme.

Pattern matching

Core then is to have a pattern matching framework in the language: A way of asking if a piece of data has a particular shape, and if so, extracting pieces of it.

if (o is Point(var x, 5)) ...

There are probably at least two ways you want to use "patterns":

  1. As part of an expression, where the result is a bool signaling whether the pattern matched a given value, and where variables in the pattern are in scope throughout the statement in which the pattern occurs.
  2. As a case in a switch statement, where the case is picked if the pattern matches, and the variables in the pattern are in scope throughout the statements of that case.

A strong candidate syntax for the expression syntax is a generalization of the is expression: we consider the type in an is expression just a special case, and start allowing any pattern on the right hand side. Thus, the following would be valid is expressions:

if (o is Point(*, 5) p) Console.WriteLine(o.x);
if (o is Point p) Console.WriteLine(p.x);
if (p is (var x, 5) ...

Variable declarations in an expression would have the same scope questions as declaration expressions did.

A strong candidate for the switch syntax is to simply generalize current switch statements so that

  • the switch expression can be any type
  • the case labels can contain patterns, not just constants
  • the cases are checked in order of appearance, since they can now overlap
switch (o) {
case string s:
    Console.WriteLine(s);
    break;
case int i:
    Console.WriteLine($"Number {i}");
    break;
case Point(int x, int y):
    Console.WriteLine("({x},{y})");
    break;
case null:
    Console.WriteLine("<null>);
    break
}

Other syntaxes you can think of:

Expression-based switch: An expression form where you can have multiple cases, each producing a result value of the same type.

Unconditional deconstruction: It might be useful to separate the deconstruction functionality out from the checking, and be able to unconditionally extract parts from a value that you know the type of:

(var x, var y) = getPoint();

There is a potential issue here where the value could be null, and there's no check for it. It's probably ok to have a null reference exception in this case.

It would be a design goal to have symmetry between construction and deconstruction syntaxes.

Patterns at least have type testing, value comparison and deconstruction aspects to them.

There may be ways for a type to specify its deconstruction syntax.

In addition it is worth considering something along the lines of "active patterns", where a type can specify logic to determine whether a pattern applies to it or not.

Imagine positional deconstruction or active patterns could be expressed with certain methods:

class Point {
    public Point(int x, int y) {...}
    void Deconstruct(out int x, out int y) { ... }
    static bool Match(Point p, out int x, out int y) ...
    static bool Match(JObject json, out int x, out int y) ...
}

We could imagine separate syntax for specifying this.

One pattern that does not put new requirements on the type is matching against properties/fields:

if (o is Point { X is var x, Y is 0 }) ...

Open question: are the variables from patterns mutable?

This has a strong similarity to declaration expressions, and they could coexist, with shared scope rules.

Records

Let's not go deep on records now, but we are aware that we need to reconcile them with primary constructors, as well as with pattern matching.

Array Slices

One feature that could lead to a lot of efficiency would be the ability to have "windows" into arrays - or even onto unmanaged swaths of memory passed along through interop. The amount of copying that could be avoided in some scenarios is probably very significant.

Array slices represent an interesting design dilemma between performance and usability. There is nothing about an array slice that is functionally different from an array: You can get its length and access its elements. For all intents and purposes they are indistinguishable. So the best user experience would certainly be that slices just are arrays - that they share the same type. That way, all the existing code that operates on arrays can work on slices too, without modification.

Of course this would require quite a change to the runtime. The performance consequences of that could be negative even on the existing kind of arrays. As importantly, slices themselves would be more efficiently represented by a struct type, and for high-perf scenarios, having to allocate a heap object for them might be prohibitive.

One intermediate approach might be to have slices be a struct type Slice, but to let it implicitly convert to T[] in such a way that the underlying storage is still shared. That way you can use Slice for high performance slice manipulation (e.g. in recursive algorithms where you keep subdividing), but still make use of existing array-based APIs at the cost of a boxing-like conversion allocating a small object.

ref locals and ref returns

Just like the language today has ref parameters, we could allow locals and even return values to be by ref. This would be particularly useful for interop scenarios, but could in general help avoid copying. Essentially you could return a "safe pointer" e.g. to a slot in an array.

The runtime already fully allows this, so it would just be a matter of surfacing it in the language syntax. It may come with a significant conceptual burden, however. If a method call can return a variable as opposed to a value, does that mean you can now assign to it?:

m(x, y) = 5;

You can now imagine getter-only properties or indexers returning refs that can be assigned to. Would this be quite confusing?

There would probably need to be some pretty restrictive guidelines about how and why this is used.

readonly parameters and locals

Parameters and locals can be captured by lambdas and thereby accessed concurrently, but there's no way to protect them from shared-mutual-state issues: they can't be readonly.

In general, most parameters and many locals are never intended to be assigned to after they get their initial value. Allowing readonly on them would express that intent clearly.

One problem is that this feature might be an "attractive nuisance". Whereas the "right thing" to do would nearly always be to make parameters and locals readonly, it would clutter the code significantly to do so.

An idea to partly alleviate this is to allow the combination readonly var on a local variable to be contracted to val or something short like that. More generally we could try to simply think of a shorter keyword than the established readonly to express the readonly-ness.

Lambda capture lists

Lambda expressions can refer to enclosing variables:

var name = GetName();
var query = customers.Where(c => c.Name == name);

This has a number of consequences, all transparent to the developer:

  • the local variable is lifted to a field in a heap-allocated object
  • concurrent runs of the lambda may access and even modify the field at the same time
  • because of implementation tradeoffs the content of the variable may be kept live by the GC, sometimes even after lambdas directly using them cease to exist.

For these reasons, the recently introduced lambdas in C++ offer the possibility for a lambda to explicitly specify what can be captured (and how). We could consider a similar feature, e.g.:

var name = GetName();
var query = customers.Where([name]c => c.Name == name);

This ensures that the lambda only captures name and no other variable. In a way the most useful annotation would be the empty [], making sure that the lambda is never accidentally modified to capture anything.

One problem is that it frankly looks horrible. There are probably other syntaxes we could consider. Indeed we need to think about the possibility that we would ever add nested functions or class declarations: whatever capture specification syntax we come up with would have to also work for them.

C# always captures "by reference": the lambda can observe and effect changes to the original variable. An option with capture lists would be to allow other modes of capture, notable "by value", where the variable is copied rather than lifted:

var name = GetName();
var query = customers.Where([val name]c => c.Name == name);

This might not be too useful, as it has the same effect as introducing another local initialized to the value of the original one, and then capture that instead.

If we don't want capture list as a full-blown feature, we could consider allowing attributes on lambdas and then having a Roslyn analyzer check that the capture is as specified.

Method contracts

.NET already has a contract system, that allows annotation of methods with pre- and post-conditions. It grew out of the Spec# research project, and requires post-compile IL rewriting to take full effect. Because it has no language syntax, specifying the contracts can get pretty ugly.

It has often been proposed that we should add specific contract syntax:

public void Remove(string item)
    requires item != null
    ensures Count >= 0
{
   ...
}

One radical idea is for these contracts to be purely runtime enforced: they would simply turn into checks throwing exceptions (or FailFast'ing - an approach that would need further discussion, but seems very attractive).

When you think about how much code is currently occupied with arguments and result checking, this certainly seems like an attractive way to reduce code bloat and improve readability.

Furthermore, the contracts can produce metadata that can be picked up and displayed by tools.

You could imagine dedicated syntax for common cases - notably null checks. Maybe that is the way we get some non-nullability into the system?

@svick

This comment has been minimized.

Show comment
Hide comment
@svick

svick Jan 27, 2015

Contributor

So, array slices are basically an improved version of ArraySegment<T>?

Contributor

svick commented Jan 27, 2015

So, array slices are basically an improved version of ArraySegment<T>?

@s-aida

This comment has been minimized.

Show comment
Hide comment
@s-aida

s-aida Jan 28, 2015

While Pattern matching is awesome, I'm still disappointed to know that break; is mandatory. I'm pretty sure you've had lots of discussions in codeplex or elsewhere (which I've never read) so no need for rationale or history behind this, but that's about the only part of C# which I have to feel is lamely designed.

s-aida commented Jan 28, 2015

While Pattern matching is awesome, I'm still disappointed to know that break; is mandatory. I'm pretty sure you've had lots of discussions in codeplex or elsewhere (which I've never read) so no need for rationale or history behind this, but that's about the only part of C# which I have to feel is lamely designed.

@MadsTorgersen

This comment has been minimized.

Show comment
Hide comment
@MadsTorgersen

MadsTorgersen Jan 28, 2015

Contributor

@svick: Yeah I guess. With possible language support.
@shunsukeaida: this is totally a straw man syntax - we aren't even close to settling on a specific syntax. However, if we want to fit pattern matching into switch, we have to kind of blend in well. One might argue that we could just get rid of break, and make it implicit and the end of a case block. That's certainly worth considering.

Contributor

MadsTorgersen commented Jan 28, 2015

@svick: Yeah I guess. With possible language support.
@shunsukeaida: this is totally a straw man syntax - we aren't even close to settling on a specific syntax. However, if we want to fit pattern matching into switch, we have to kind of blend in well. One might argue that we could just get rid of break, and make it implicit and the end of a case block. That's certainly worth considering.

@theoy theoy added this to the Unknown milestone Jan 28, 2015

@agocke

This comment has been minimized.

Show comment
Hide comment
@agocke

agocke Jan 28, 2015

Contributor

@MadsTorgersen Something not mentioned in relation to pattern matching: have discriminated unions been considered?

Contributor

agocke commented Jan 28, 2015

@MadsTorgersen Something not mentioned in relation to pattern matching: have discriminated unions been considered?

@ashmind

This comment has been minimized.

Show comment
Hide comment
@ashmind

ashmind Jan 28, 2015

@MadsTorgersen @shunsukeaida I proposed a better switch syntax on CodePlex (independently from destructuring) https://roslyn.codeplex.com/discussions/565550, though it might not work with labels.

I think a better switch syntax is rather important whether pattern matching gets into the language or not.

ashmind commented Jan 28, 2015

@MadsTorgersen @shunsukeaida I proposed a better switch syntax on CodePlex (independently from destructuring) https://roslyn.codeplex.com/discussions/565550, though it might not work with labels.

I think a better switch syntax is rather important whether pattern matching gets into the language or not.

@ashmind

This comment has been minimized.

Show comment
Hide comment
@ashmind

ashmind Jan 28, 2015

Anyone can put a feature idea up as an issue on GitHub. We'll keep an eye on those, and use them as input to language design.

Is there some way to distinguish those from e.g. Roslyn bugs? E.g. some title prefix or so? Labels are good but I can't assign them when reporting the issue I believe.

And I think it would be cool to have some minimal format for it, e.g. at least require the description of the problem, a proposed solution, some use cases using the proposed solution. BCL team seems to be doing a good job there with speclets.

Also I think some certain yes/no on proposals would be great, so that any GitHub proposal is not only considered, but either rejected, accepted for prototyping, or sent to rework due to some design constraints. Again I think BCL team is doing great with their periodic review approach.

ashmind commented Jan 28, 2015

Anyone can put a feature idea up as an issue on GitHub. We'll keep an eye on those, and use them as input to language design.

Is there some way to distinguish those from e.g. Roslyn bugs? E.g. some title prefix or so? Labels are good but I can't assign them when reporting the issue I believe.

And I think it would be cool to have some minimal format for it, e.g. at least require the description of the problem, a proposed solution, some use cases using the proposed solution. BCL team seems to be doing a good job there with speclets.

Also I think some certain yes/no on proposals would be great, so that any GitHub proposal is not only considered, but either rejected, accepted for prototyping, or sent to rework due to some design constraints. Again I think BCL team is doing great with their periodic review approach.

@jods4

This comment has been minimized.

Show comment
Hide comment
@jods4

jods4 Jan 28, 2015

Here's a theme suggestion.

With multi-core becoming commonplace and the nice Task and TPL apis, I see more and more concurrent programming, even if just a few (2-3) background threads.

It would be very nice to get some help from C# regarding correctness here. Immutable data structures are a big help, but for mutable data the main issue is that nothing formally indicates which thread has data ownership, or if data is shared in a read-only fashion, or if data must be accessed inside a lock. In complex programs and large teams this stuff is really hard to get right.

C# was a rockstar when it introduced async to help write asynchronous code. I know it's a very hard topic but I would love to see C# bring safe concurrent programming to mainstream.

jods4 commented Jan 28, 2015

Here's a theme suggestion.

With multi-core becoming commonplace and the nice Task and TPL apis, I see more and more concurrent programming, even if just a few (2-3) background threads.

It would be very nice to get some help from C# regarding correctness here. Immutable data structures are a big help, but for mutable data the main issue is that nothing formally indicates which thread has data ownership, or if data is shared in a read-only fashion, or if data must be accessed inside a lock. In complex programs and large teams this stuff is really hard to get right.

C# was a rockstar when it introduced async to help write asynchronous code. I know it's a very hard topic but I would love to see C# bring safe concurrent programming to mainstream.

@daveaglick

This comment has been minimized.

Show comment
Hide comment
@daveaglick

daveaglick Jan 28, 2015

Contributor

I would love to see more native language support for metaprogramming. Mixins and/or traits would certainly help with code reuse challenges and I would personally love to see one of these make it in. While T4 templates work okay for code generation, they often feel clunky and hacky (for example, even though it's a tooling thing, the fact that VS addins are needed just to consistently evaluate them is a clue something is off). Some kind of language support or hook for code generation would be welcome. I would also love to see some sort of rudimentary support for aspects and weaving. PostSharp works well and has a nice API, but requires extra libraries and IL rewriting. Fody aspects work, but there's a really steep learning curve. I'm guessing full AOP support would be way out of scope for this language update, but incremental support or even hooks (perhaps in Roslyn) would be welcomed.

There was also mention of improved overload resolution. This has been a particular pain point of mine, especially when dealing with generics and extension methods. For example, it would be awesome if generic constraints were taken into account when determining a match. I understand the reason why they're not considered (I.e., not part of the method signature) but it would be great if there were a way to overcome this.

Contributor

daveaglick commented Jan 28, 2015

I would love to see more native language support for metaprogramming. Mixins and/or traits would certainly help with code reuse challenges and I would personally love to see one of these make it in. While T4 templates work okay for code generation, they often feel clunky and hacky (for example, even though it's a tooling thing, the fact that VS addins are needed just to consistently evaluate them is a clue something is off). Some kind of language support or hook for code generation would be welcome. I would also love to see some sort of rudimentary support for aspects and weaving. PostSharp works well and has a nice API, but requires extra libraries and IL rewriting. Fody aspects work, but there's a really steep learning curve. I'm guessing full AOP support would be way out of scope for this language update, but incremental support or even hooks (perhaps in Roslyn) would be welcomed.

There was also mention of improved overload resolution. This has been a particular pain point of mine, especially when dealing with generics and extension methods. For example, it would be awesome if generic constraints were taken into account when determining a match. I understand the reason why they're not considered (I.e., not part of the method signature) but it would be great if there were a way to overcome this.

@jods4

This comment has been minimized.

Show comment
Hide comment
@jods4

jods4 Jan 28, 2015

+1 AOP can have lots of useful applications, some of them have been requested by the community for a long time. A few examples:

  • Automatic INotifyPropertyChanged implementation.
  • Automatic check for null argument on public methods (I think the ASP.NET team does that for vNext).
  • Automatic implementation of a "IsDirty" flag on model entities.
  • Wrapping logging, security or error handlers around services.
    And the list goes on!

jods4 commented Jan 28, 2015

+1 AOP can have lots of useful applications, some of them have been requested by the community for a long time. A few examples:

  • Automatic INotifyPropertyChanged implementation.
  • Automatic check for null argument on public methods (I think the ASP.NET team does that for vNext).
  • Automatic implementation of a "IsDirty" flag on model entities.
  • Wrapping logging, security or error handlers around services.
    And the list goes on!
@svick

This comment has been minimized.

Show comment
Hide comment
@svick

svick Jan 28, 2015

Contributor

@jods4 Of interest may be the fact that @jaredpar, one of the coauthors of the Uniqueness and Reference Immutability for Safe Parallelism paper, is now on the Roslyn team and said:

a part of my job will be trying to integrate some of the goodness that came out of our research back into the language

Contributor

svick commented Jan 28, 2015

@jods4 Of interest may be the fact that @jaredpar, one of the coauthors of the Uniqueness and Reference Immutability for Safe Parallelism paper, is now on the Roslyn team and said:

a part of my job will be trying to integrate some of the goodness that came out of our research back into the language

@bojanrajkovic

This comment has been minimized.

Show comment
Hide comment
@bojanrajkovic

bojanrajkovic Jan 28, 2015

I think let might be a useful convention for readonly var, or even const, though that's probably leading too much down the path of too many meanings for const that C/C++ fall prey to.

bojanrajkovic commented Jan 28, 2015

I think let might be a useful convention for readonly var, or even const, though that's probably leading too much down the path of too many meanings for const that C/C++ fall prey to.

@biboudis

This comment has been minimized.

Show comment
Hide comment
@biboudis

biboudis Jan 28, 2015

@MadsTorgersen What do you mean by delegation? Following theories of incomplete objects that get composed by delegation with proper late-binding semantics maybe?

biboudis commented Jan 28, 2015

@MadsTorgersen What do you mean by delegation? Following theories of incomplete objects that get composed by delegation with proper late-binding semantics maybe?

@alexpolozov

This comment has been minimized.

Show comment
Hide comment
@alexpolozov

alexpolozov Jan 28, 2015

Something not mentioned in relation to pattern matching: have discriminated unions been considered?

@agocke Yes, in the original proposal discriminated unions were implemented with record subclassing — similar to case classes in Scala.

@MadsTorgersen By the way, regarding the state of pattern matching. If you do decide to consider them for C# 7, will the first prototype be merged into the current Github source as a feature branch? Will the community be able to contribute to such prototypes? I think you've mentioned before that you want to simplify the PR process in a couple of months, so that external feature prototyping/bug fixing would be easier.

alexpolozov commented Jan 28, 2015

Something not mentioned in relation to pattern matching: have discriminated unions been considered?

@agocke Yes, in the original proposal discriminated unions were implemented with record subclassing — similar to case classes in Scala.

@MadsTorgersen By the way, regarding the state of pattern matching. If you do decide to consider them for C# 7, will the first prototype be merged into the current Github source as a feature branch? Will the community be able to contribute to such prototypes? I think you've mentioned before that you want to simplify the PR process in a couple of months, so that external feature prototyping/bug fixing would be easier.

@Romoku

This comment has been minimized.

Show comment
Hide comment
@Romoku

Romoku Jan 28, 2015

Non-nullable reference types might be better served with another approach. Assess the feasibility of CLR support for describing a non-null reference type. Even if C# cannot currently take advantage of non-nullable reference types there might be another .NET language that figures out how.

Romoku commented Jan 28, 2015

Non-nullable reference types might be better served with another approach. Assess the feasibility of CLR support for describing a non-null reference type. Even if C# cannot currently take advantage of non-nullable reference types there might be another .NET language that figures out how.

@horaciojcfilho

This comment has been minimized.

Show comment
Hide comment
@horaciojcfilho

horaciojcfilho Jan 28, 2015

Not about new Visual Basic language version? Is Visual Basic coming to die or was placed in the background? I love VB and I'd love to hear about new features 😄 for it.

horaciojcfilho commented Jan 28, 2015

Not about new Visual Basic language version? Is Visual Basic coming to die or was placed in the background? I love VB and I'd love to hear about new features 😄 for it.

@Romoku

This comment has been minimized.

Show comment
Hide comment
@Romoku

Romoku Jan 28, 2015

@HoracioFilho They mentioned that they are planning the features for both C# and VB right now.

To begin with, we meet 4 hours a week as we decide on the overall focus areas. There will not be a separate Visual Basic design meeting during this initial period, as many of the overall decisions are likely to apply to both and need to happen in concert.

Romoku commented Jan 28, 2015

@HoracioFilho They mentioned that they are planning the features for both C# and VB right now.

To begin with, we meet 4 hours a week as we decide on the overall focus areas. There will not be a separate Visual Basic design meeting during this initial period, as many of the overall decisions are likely to apply to both and need to happen in concert.

@damageboy

This comment has been minimized.

Show comment
Hide comment
@damageboy

damageboy Jan 28, 2015

One thing HPC like devs could benefit from, is have the ability to "construct" primitive array types off of memory mapped file, like java's nio supports...

I realize this is mainly a runtime feature, rather than a c# feature, is there a "better" place to discuss those?

damageboy commented Jan 28, 2015

One thing HPC like devs could benefit from, is have the ability to "construct" primitive array types off of memory mapped file, like java's nio supports...

I realize this is mainly a runtime feature, rather than a c# feature, is there a "better" place to discuss those?

@jvlppm

This comment has been minimized.

Show comment
Hide comment
@jvlppm

jvlppm Jan 28, 2015

Something that I missed before, and reactive extensions almost gave it to me:
Ability to combine await and yield, resulting in an asynchronous sequence.

async var GenerateNumbers()
{
    int i = 0;
    while (true)
    {
        await Task.Delay(TimeSpan.FromSeconds(1));
        yield return i++;
    }
}

foreach(await int item in GenerateNumbers())
{
    WriteLine("Item: \{item}");
}

jvlppm commented Jan 28, 2015

Something that I missed before, and reactive extensions almost gave it to me:
Ability to combine await and yield, resulting in an asynchronous sequence.

async var GenerateNumbers()
{
    int i = 0;
    while (true)
    {
        await Task.Delay(TimeSpan.FromSeconds(1));
        yield return i++;
    }
}

foreach(await int item in GenerateNumbers())
{
    WriteLine("Item: \{item}");
}
@jonpryor

This comment has been minimized.

Show comment
Hide comment
@jonpryor

jonpryor Jan 28, 2015

readonly var could be abbreviated as let, which is already a contextual keyword within query comprehension expressions and semantically declares a readonly variable...

jonpryor commented Jan 28, 2015

readonly var could be abbreviated as let, which is already a contextual keyword within query comprehension expressions and semantically declares a readonly variable...

@Cellivar

This comment has been minimized.

Show comment
Hide comment
@Cellivar

Cellivar Jan 28, 2015

+1 to the Contracts system. There's already a similar concept with type constraints for generics.

class Foo<T> where T : IComparable { }

An extension to this pattern for functions would be intuitive to anyone familiar with constraints.

void Foo(string bar) where bar != null, Count > 0 { }

Would separate 'ensures' and 'requires' actually be necessary? Could the statement be inferred?

Cellivar commented Jan 28, 2015

+1 to the Contracts system. There's already a similar concept with type constraints for generics.

class Foo<T> where T : IComparable { }

An extension to this pattern for functions would be intuitive to anyone familiar with constraints.

void Foo(string bar) where bar != null, Count > 0 { }

Would separate 'ensures' and 'requires' actually be necessary? Could the statement be inferred?

@MadsTorgersen

This comment has been minimized.

Show comment
Hide comment
@MadsTorgersen

MadsTorgersen Jan 28, 2015

Contributor

@agocke: We are trying to come up with a general framework for pattern matching over classes. Ideally there wouldn't be a new "thing" in the language called algebraic datatypes or discriminated unions. Instead there may be compact syntax for describing hierarchies of simple value types (that would happen to be well suited for pattern matching) as a shorthand for class declarations.

@ashmind: yeah, maybe it's time to revamp switch. Worth a think.

@ashmind: I think we'll go through and apply the labels. We'll build up our processes as we go - the BCL team are ahead of us here, and we'll draw on their experiences.

@jods4: safe concurrency is an insanely hard problem. Not sure what we'd do.

@somedave: realistically I can't see us doing something about metaprogramming at the language level before we have a very clear idea of a) what would really move the needle for a lot of developers, and b) how to deal with it at a tooling level.

@jods4: AOP is notoriously hard to reason about for a developer. It's a very big hammer.

@bojanrajkovic, @jonpryor: let is certainly a candidate.

@biboudis: Full-blown delegation (with this-binding and everything) seems out of the question - that's a whole alternative mechanism to inheritance. But some languages have this nifty little implement-interface-by-redirection trick. Not sure if it's worthwhile or even sane.

@apskim: No clear decisions yet as to how we will share prototypes. For the time being, most of the team is heads down making the upcoming version great. This further-out blue sky stuff won't get priority until that's out the door. Yes, would be great to involve the community in prototyping.

@Romoku: I'd love to get non-nullable reference types into the CLR. It seems a tall order even there: what's inside an array of non-nullable reference type when it's first created? etc.

@HoracioFilho: Yeah, @Romoku has it right. It doesn't make sense to start separate efforts. Let's get the broad brushstrokes worked out and then split out to language-lawyer on the syntax. :-)

@damageboy: If I understand you correctly, yes that would be an awesome interop feature: Array-typed "windows" onto native data. Privileged (and unsafe!) methods would create them, but be able to share them with your code.

@jvlppm: Yes! When I mentioned "async sequences" this is the kind of thing I had in mind.

@MrDoomBringer: Whatever the syntax, it would certainly occupy the same "region" of a method declaration as the constraints. Maybe it makes sense to merge them.

Contributor

MadsTorgersen commented Jan 28, 2015

@agocke: We are trying to come up with a general framework for pattern matching over classes. Ideally there wouldn't be a new "thing" in the language called algebraic datatypes or discriminated unions. Instead there may be compact syntax for describing hierarchies of simple value types (that would happen to be well suited for pattern matching) as a shorthand for class declarations.

@ashmind: yeah, maybe it's time to revamp switch. Worth a think.

@ashmind: I think we'll go through and apply the labels. We'll build up our processes as we go - the BCL team are ahead of us here, and we'll draw on their experiences.

@jods4: safe concurrency is an insanely hard problem. Not sure what we'd do.

@somedave: realistically I can't see us doing something about metaprogramming at the language level before we have a very clear idea of a) what would really move the needle for a lot of developers, and b) how to deal with it at a tooling level.

@jods4: AOP is notoriously hard to reason about for a developer. It's a very big hammer.

@bojanrajkovic, @jonpryor: let is certainly a candidate.

@biboudis: Full-blown delegation (with this-binding and everything) seems out of the question - that's a whole alternative mechanism to inheritance. But some languages have this nifty little implement-interface-by-redirection trick. Not sure if it's worthwhile or even sane.

@apskim: No clear decisions yet as to how we will share prototypes. For the time being, most of the team is heads down making the upcoming version great. This further-out blue sky stuff won't get priority until that's out the door. Yes, would be great to involve the community in prototyping.

@Romoku: I'd love to get non-nullable reference types into the CLR. It seems a tall order even there: what's inside an array of non-nullable reference type when it's first created? etc.

@HoracioFilho: Yeah, @Romoku has it right. It doesn't make sense to start separate efforts. Let's get the broad brushstrokes worked out and then split out to language-lawyer on the syntax. :-)

@damageboy: If I understand you correctly, yes that would be an awesome interop feature: Array-typed "windows" onto native data. Privileged (and unsafe!) methods would create them, but be able to share them with your code.

@jvlppm: Yes! When I mentioned "async sequences" this is the kind of thing I had in mind.

@MrDoomBringer: Whatever the syntax, it would certainly occupy the same "region" of a method declaration as the constraints. Maybe it makes sense to merge them.

@agocke

This comment has been minimized.

Show comment
Hide comment
@agocke

agocke Jan 28, 2015

Contributor

@apskim @MadsTorgersen

Technically, you're correct, but there are actually no new features there specific to discriminated unions. :) The method of generating discriminated unions in the draft specification has one (to me very significant) weakness -- it has no specification of completeness checking in switch statements right now. One way to add this would be to add specialized support just for discriminated unions, for example coming up with a new syntax for enum which desugers to a discriminated union behind the scenes.

Alternatively, I would like a spec addition that guarantees that the following data structure provides completeness checking in a switch statement.

public abstract class BinaryTree<T>
{
  private BinaryTree() {};
  public sealed class Leaf<U>() : BinaryTree<U>;
  public sealed class Node<U>(U item, U left, U right) : BinaryTree<U>;
}

It's a little more heavyweight than some de novo syntax, but it works. Obviously I would be volunteering to implement this warning. ;)

Contributor

agocke commented Jan 28, 2015

@apskim @MadsTorgersen

Technically, you're correct, but there are actually no new features there specific to discriminated unions. :) The method of generating discriminated unions in the draft specification has one (to me very significant) weakness -- it has no specification of completeness checking in switch statements right now. One way to add this would be to add specialized support just for discriminated unions, for example coming up with a new syntax for enum which desugers to a discriminated union behind the scenes.

Alternatively, I would like a spec addition that guarantees that the following data structure provides completeness checking in a switch statement.

public abstract class BinaryTree<T>
{
  private BinaryTree() {};
  public sealed class Leaf<U>() : BinaryTree<U>;
  public sealed class Node<U>(U item, U left, U right) : BinaryTree<U>;
}

It's a little more heavyweight than some de novo syntax, but it works. Obviously I would be volunteering to implement this warning. ;)

@Romoku

This comment has been minimized.

Show comment
Hide comment
@Romoku

Romoku Jan 28, 2015

@MadsTorgersen From what I understand of how Eric Lippert and Spec# describe non-null references types the contents of the array cannot be null when observed during the program execution. This means that up to the point of observance the state can be undetermined, but there must be a value at the point of observance.

Romoku commented Jan 28, 2015

@MadsTorgersen From what I understand of how Eric Lippert and Spec# describe non-null references types the contents of the array cannot be null when observed during the program execution. This means that up to the point of observance the state can be undetermined, but there must be a value at the point of observance.

@jamesbascle

This comment has been minimized.

Show comment
Hide comment
@jamesbascle

jamesbascle Jan 28, 2015

@somedave Agreed on the AOP.

PostSharp is amazing, but It can be hard to keep it working right on a large team of developers, and it's pretty expensive for all the bells and whistles for a large team as well, even if only a couple of people are even writing the code.

Some language level support for generic argument and result manipulation on methods (like OnMethodBoundaryAspect) alone would be a huge step to meeting the need for metaprogramming and supplanting the wonkiness of PostSharp.

jamesbascle commented Jan 28, 2015

@somedave Agreed on the AOP.

PostSharp is amazing, but It can be hard to keep it working right on a large team of developers, and it's pretty expensive for all the bells and whistles for a large team as well, even if only a couple of people are even writing the code.

Some language level support for generic argument and result manipulation on methods (like OnMethodBoundaryAspect) alone would be a huge step to meeting the need for metaprogramming and supplanting the wonkiness of PostSharp.

@jamesbascle

This comment has been minimized.

Show comment
Hide comment
@jamesbascle

jamesbascle Jan 28, 2015

@MadsTorgersen
The PostSharp VisualStudio add-on seems to allow one to easily see which methods are manipulated by which aspects, both at the "This Method is manipulated by This Aspect" and "This Aspect manipulates These Methods".

We've saved ourselves literally thousands of lines of code, but it gets pretty expensive and it's a pretty obscure technical expense that is hard to justify to management types.

It would also be nice if C#/.NET were the first language/framework with anything approaching the kind of reach it has to REALLY have built-in first-class AOP facilities in a language. AspectJ and the things found in some of the dynamic languages or the LISPs are not exactly getting a lot of attention these days. I'd personally see it as a huge huge win for this new open design process from a developer perspective.

jamesbascle commented Jan 28, 2015

@MadsTorgersen
The PostSharp VisualStudio add-on seems to allow one to easily see which methods are manipulated by which aspects, both at the "This Method is manipulated by This Aspect" and "This Aspect manipulates These Methods".

We've saved ourselves literally thousands of lines of code, but it gets pretty expensive and it's a pretty obscure technical expense that is hard to justify to management types.

It would also be nice if C#/.NET were the first language/framework with anything approaching the kind of reach it has to REALLY have built-in first-class AOP facilities in a language. AspectJ and the things found in some of the dynamic languages or the LISPs are not exactly getting a lot of attention these days. I'd personally see it as a huge huge win for this new open design process from a developer perspective.

@damageboy

This comment has been minimized.

Show comment
Hide comment
@damageboy

damageboy Jan 28, 2015

@MadsTorgersen you understood me perfectly

damageboy commented Jan 28, 2015

@MadsTorgersen you understood me perfectly

@ufcpp

This comment has been minimized.

Show comment
Hide comment
@ufcpp

ufcpp Jan 28, 2015

How about Units of Measure (like F#)?
From the perspective of performance and convenience, I prefer to use primitive values (int ,byte, etc.), but at the same time, from the perspective of reliability, I'd like to use user-defined values. This dilemma can be solved by Units of Measure.

ufcpp commented Jan 28, 2015

How about Units of Measure (like F#)?
From the perspective of performance and convenience, I prefer to use primitive values (int ,byte, etc.), but at the same time, from the perspective of reliability, I'd like to use user-defined values. This dilemma can be solved by Units of Measure.

@chrisaut

This comment has been minimized.

Show comment
Hide comment
@chrisaut

chrisaut Jan 28, 2015

+1 for Contracts from me.

The current solution seems half hearted and not really supported very well.

Non-nullness could IMO be left to contracts, IF there is a static analyzer that can enforce this.

If they are to become a language feature, common cases could be expressed much shorter.
I'm thinking something like:

public int >= 0 M(string! s, double < 100 y, MyClass c)
    requires c.IsInitialized
{}

which would be the same as

public int M(string s, double y)
    requires s != null,
    requires y < 100,
    requires c..IsInitialized
    ensures result >= 0
{}

However it could looks a bit messy. Thoughts?

PS: the example

if (o is Point(*, 5) p) Console.WriteLine(o.x);

should that be

if (o is Point(*, 5) p) Console.WriteLine(p.x);

or am I misunderstanding?

chrisaut commented Jan 28, 2015

+1 for Contracts from me.

The current solution seems half hearted and not really supported very well.

Non-nullness could IMO be left to contracts, IF there is a static analyzer that can enforce this.

If they are to become a language feature, common cases could be expressed much shorter.
I'm thinking something like:

public int >= 0 M(string! s, double < 100 y, MyClass c)
    requires c.IsInitialized
{}

which would be the same as

public int M(string s, double y)
    requires s != null,
    requires y < 100,
    requires c..IsInitialized
    ensures result >= 0
{}

However it could looks a bit messy. Thoughts?

PS: the example

if (o is Point(*, 5) p) Console.WriteLine(o.x);

should that be

if (o is Point(*, 5) p) Console.WriteLine(p.x);

or am I misunderstanding?

@TuckerDowns

This comment has been minimized.

Show comment
Hide comment
@TuckerDowns

TuckerDowns Jan 28, 2015

Sorry to leave a non constructive comment here but I do have a question related to this process of specifying and designing C#7.

I am interested in watching and studying this process and caught the note about this being broadcast live. What is the best way to follow along and maybe watch these meetings?

I am an undergraduate student who just started a class on language specifications and design and it's definitely one of the most interesting topics I have studied thus far. I don't really use c# but I am really interested in studying the process of language design and that seems to be (partly or mostly) independent of the exact language being specified. I also think it's important, for my benefit, to note here that C# is obviously not a new language and so there is a lot of base work being built upon here.

Anyway, with that said I would love to follow along as closely as I can and would like some more information about how to do that.

Thanks, -TuckerD

TuckerDowns commented Jan 28, 2015

Sorry to leave a non constructive comment here but I do have a question related to this process of specifying and designing C#7.

I am interested in watching and studying this process and caught the note about this being broadcast live. What is the best way to follow along and maybe watch these meetings?

I am an undergraduate student who just started a class on language specifications and design and it's definitely one of the most interesting topics I have studied thus far. I don't really use c# but I am really interested in studying the process of language design and that seems to be (partly or mostly) independent of the exact language being specified. I also think it's important, for my benefit, to note here that C# is obviously not a new language and so there is a lot of base work being built upon here.

Anyway, with that said I would love to follow along as closely as I can and would like some more information about how to do that.

Thanks, -TuckerD

@RxDave

This comment has been minimized.

Show comment
Hide comment
@RxDave

RxDave Jan 28, 2015

👍 Method contracts! Yes please. Also really liked the idea of syntax for tuples and records. Pattern matching - even if just within switch case statements - would be fantastic as well.

RxDave commented Jan 28, 2015

👍 Method contracts! Yes please. Also really liked the idea of syntax for tuples and records. Pattern matching - even if just within switch case statements - would be fantastic as well.

@jonschoning

This comment has been minimized.

Show comment
Hide comment
@jonschoning

jonschoning Jan 28, 2015

If the 'pattern on the right hand side' allows matching on subclasses of the data on the left of this 'is' operator, then without having the capability of exhaustive checks, (as seems from what is described so far), imho the most important benefits of pattern matching are lost.

An 'exhaustive check' would mean that there would be a compiler warning if new subclasses were added to the data being matched on that didn't have cases for that new subclass in existing pattern matching code.

Actually, I'm thinking now it may be difficult to make exhaustive checks work for concrete classes because of ambiguity with the syntax allowing destructuring of the primary constructor.

Perhaps there could exhaustive checks only when matching on abstract classes if that causes less ambiguity.

Thinking about it some more, classes are not closed, so exhaustive checks would make no sense... This really is not what I would call pattern matching without discriminated unions. This should just be called "destructuring"

jonschoning commented Jan 28, 2015

If the 'pattern on the right hand side' allows matching on subclasses of the data on the left of this 'is' operator, then without having the capability of exhaustive checks, (as seems from what is described so far), imho the most important benefits of pattern matching are lost.

An 'exhaustive check' would mean that there would be a compiler warning if new subclasses were added to the data being matched on that didn't have cases for that new subclass in existing pattern matching code.

Actually, I'm thinking now it may be difficult to make exhaustive checks work for concrete classes because of ambiguity with the syntax allowing destructuring of the primary constructor.

Perhaps there could exhaustive checks only when matching on abstract classes if that causes less ambiguity.

Thinking about it some more, classes are not closed, so exhaustive checks would make no sense... This really is not what I would call pattern matching without discriminated unions. This should just be called "destructuring"

@Plasma

This comment has been minimized.

Show comment
Hide comment
@Plasma

Plasma Jan 28, 2015

Thanks @MadsTorgersen - a few various thoughts:

  1. Introducing a "type safe basic type". Let me distinguish between:
    int customerId = 1;
    int billingId = 1;

Can we "mark" this integer (or Guid, or long, etc) of customerId (pulled from a database for example) as a "CustomerId" type?

public CustomerId : int { }

I want to prevent a method that takes:

public void Charge(int customerId)

... accidentally being passed in a billingId (also an INT).

Ideally my interface was:

public void Charge(CustomerId customerId)

... and passing in a billingId would result in a compilation error.

  1. +1 to AOP integration (we use Castle Dynamic proxies right now). Really useful for authorization, caching, logging, ... cross cutting concerns. I think the developer experience is understandable when the debugger correctly shows the stack trace of calls being intercepted (which is what happens with Castle Windsor).
  2. Ahead-of-time compilation of an entire ASP.NET website. I don't know if this is applicable to C# spec, but a way to "100% warm up, pre-load, be ready" a .NET website would be good. Right now even under Release mode, there is JIT, which I never want on a production site.
  3. Let me use commas (or spaces for universal support?) in number definitions:
    const int BigNum = 1,000,000; // No need to count the zeros
  4. I use Tuple<string, int> etc a fair bit; ideally there was a super nice way to return a tuple with named fields vs Item1/Item2 without making a new class

Plasma commented Jan 28, 2015

Thanks @MadsTorgersen - a few various thoughts:

  1. Introducing a "type safe basic type". Let me distinguish between:
    int customerId = 1;
    int billingId = 1;

Can we "mark" this integer (or Guid, or long, etc) of customerId (pulled from a database for example) as a "CustomerId" type?

public CustomerId : int { }

I want to prevent a method that takes:

public void Charge(int customerId)

... accidentally being passed in a billingId (also an INT).

Ideally my interface was:

public void Charge(CustomerId customerId)

... and passing in a billingId would result in a compilation error.

  1. +1 to AOP integration (we use Castle Dynamic proxies right now). Really useful for authorization, caching, logging, ... cross cutting concerns. I think the developer experience is understandable when the debugger correctly shows the stack trace of calls being intercepted (which is what happens with Castle Windsor).
  2. Ahead-of-time compilation of an entire ASP.NET website. I don't know if this is applicable to C# spec, but a way to "100% warm up, pre-load, be ready" a .NET website would be good. Right now even under Release mode, there is JIT, which I never want on a production site.
  3. Let me use commas (or spaces for universal support?) in number definitions:
    const int BigNum = 1,000,000; // No need to count the zeros
  4. I use Tuple<string, int> etc a fair bit; ideally there was a super nice way to return a tuple with named fields vs Item1/Item2 without making a new class
@chrisaut

This comment has been minimized.

Show comment
Hide comment
@chrisaut

chrisaut Jan 28, 2015

@Plasma, regarding your "type safe basic type", what's wrong with:

public struct CustomerId {
     public int Id { get; }
     public CustomerId(int id) { this.Id = id; }
    // optional
    public static implicit int(CustomerId cId) => cId.Id;
}

If you need many of those, you can just create an abstract base class:

public abstract class IdBase {
     public int Id { get; }
     public IdBase (int id) { this.Id = id; }
    // optional
    public static implicit int(IdBase cId) => cId.Id;
}
public class CustomerId : Idbase {}

It's not a struct anymore, but it should hardly matter.
It's a bit of typing, but with Record types and primary constructors it'll get so short it's essentially nothing.

public struct CustomerId(int id) {
     public int Id { get; } = id;
}

I believe the real feature you want is ValueType (pseudo?) - inheritance, a form a compiler enforced type aliasing.
I don't think adding yet another form of "type" to the language is a good idea, it's getting kinda complicated as it is.

chrisaut commented Jan 28, 2015

@Plasma, regarding your "type safe basic type", what's wrong with:

public struct CustomerId {
     public int Id { get; }
     public CustomerId(int id) { this.Id = id; }
    // optional
    public static implicit int(CustomerId cId) => cId.Id;
}

If you need many of those, you can just create an abstract base class:

public abstract class IdBase {
     public int Id { get; }
     public IdBase (int id) { this.Id = id; }
    // optional
    public static implicit int(IdBase cId) => cId.Id;
}
public class CustomerId : Idbase {}

It's not a struct anymore, but it should hardly matter.
It's a bit of typing, but with Record types and primary constructors it'll get so short it's essentially nothing.

public struct CustomerId(int id) {
     public int Id { get; } = id;
}

I believe the real feature you want is ValueType (pseudo?) - inheritance, a form a compiler enforced type aliasing.
I don't think adding yet another form of "type" to the language is a good idea, it's getting kinda complicated as it is.

@ryancerium

This comment has been minimized.

Show comment
Hide comment
@ryancerium

ryancerium Jan 28, 2015

Apply the readonly modifier to classes, methods, and parameters to help support immutable data structures. Perhaps add the mutable keyword for readonly classes the way C++ uses it.

I've wanted static methods in an interface before, but generic constructor constraints might have been the underlying problem.

Could you implement constraints at compile time using a static analyzer? Like using an unassigned variable is a compiler warning (error?) now.

Discriminated unions like Rust has.

Interfaces with default implementations like Java just added (is thinking about adding?). Is this different than traits or mixins in large way?

What if you make the lambda capture list an extended portion of the parameter list?

var number = GetNumber();
var name = GetName();
var query = customers.Where((c, ref name, value number) => c.Name == name || c.Number == number);`

ryancerium commented Jan 28, 2015

Apply the readonly modifier to classes, methods, and parameters to help support immutable data structures. Perhaps add the mutable keyword for readonly classes the way C++ uses it.

I've wanted static methods in an interface before, but generic constructor constraints might have been the underlying problem.

Could you implement constraints at compile time using a static analyzer? Like using an unassigned variable is a compiler warning (error?) now.

Discriminated unions like Rust has.

Interfaces with default implementations like Java just added (is thinking about adding?). Is this different than traits or mixins in large way?

What if you make the lambda capture list an extended portion of the parameter list?

var number = GetNumber();
var name = GetName();
var query = customers.Where((c, ref name, value number) => c.Name == name || c.Number == number);`
@RichiCoder1

This comment has been minimized.

Show comment
Hide comment
@RichiCoder1

RichiCoder1 Jan 28, 2015

@ryancerium
👍 for readonly class with mutable members.

What if you make the lambda capture list an extended portion of the parameter list?

var number = GetNumber();
var name = GetName();
var query = customers.Where((c, ref name, value number) => c.Name == name || c.Number == number);```

I'd be against this if only because of the cognative load of differentiating between captures and params. While [number, value name](c) => ... isn't the prettiest, it's at least a lot more immediately clear.

RichiCoder1 commented Jan 28, 2015

@ryancerium
👍 for readonly class with mutable members.

What if you make the lambda capture list an extended portion of the parameter list?

var number = GetNumber();
var name = GetName();
var query = customers.Where((c, ref name, value number) => c.Name == name || c.Number == number);```

I'd be against this if only because of the cognative load of differentiating between captures and params. While [number, value name](c) => ... isn't the prettiest, it's at least a lot more immediately clear.

@Fizzelen

This comment has been minimized.

Show comment
Hide comment
@Fizzelen

Fizzelen Jan 28, 2015

How about adding private variables inside a property, to prevent side effects from modifying the the backing field directly

public int Id
{
int _id = 0;
get { return _id; }
set { _id = value; }
}

Fizzelen commented Jan 28, 2015

How about adding private variables inside a property, to prevent side effects from modifying the the backing field directly

public int Id
{
int _id = 0;
get { return _id; }
set { _id = value; }
}

@stimpy77

This comment has been minimized.

Show comment
Hide comment
@stimpy77

stimpy77 Mar 24, 2015

Since you're discussing C# design, can you please look into an IAbortDisposable or somesuch that the compiler can pick up and clean up? To my knowledge this is a problem that has been going on since WCF premiered, where WCF clients can be in an open state or in a faulted state.

http://www.jondavis.net/techblog/post/2007/05/23/Windows-Communication-Framework-%28WCF%29-Beware-the-fake-IDisposable-implementation-!!.aspx

Perhaps a WCF client proxy could implement IAbortDisposable or IDisposeAbortable or something, and the syntax sugar to make use of it might be something like ...

using (var proxy = new MyServiceProxy()) {
    // do stuff, and then always automatically .Dispose() or .Abort()
} finally {
    if (proxy.IsFaulted) {
        // do cleanup before the CLR automatically invokes .Abort()
    }
}

This way .Dispose() always works but if it's faulted then there is an opportunity to clean up or something. I'm just brainstorming syntax ideas, but to manually do the following instead of using(..) {} ..

    if (this.State == CommunicationState.Closing ||
    this.State == CommunicationState.Closed ||
    this.State == CommunicationState.Faulted)
{
    this.Abort();
}
else
{
    this.Close();
}

.. is a nightmare.

stimpy77 commented Mar 24, 2015

Since you're discussing C# design, can you please look into an IAbortDisposable or somesuch that the compiler can pick up and clean up? To my knowledge this is a problem that has been going on since WCF premiered, where WCF clients can be in an open state or in a faulted state.

http://www.jondavis.net/techblog/post/2007/05/23/Windows-Communication-Framework-%28WCF%29-Beware-the-fake-IDisposable-implementation-!!.aspx

Perhaps a WCF client proxy could implement IAbortDisposable or IDisposeAbortable or something, and the syntax sugar to make use of it might be something like ...

using (var proxy = new MyServiceProxy()) {
    // do stuff, and then always automatically .Dispose() or .Abort()
} finally {
    if (proxy.IsFaulted) {
        // do cleanup before the CLR automatically invokes .Abort()
    }
}

This way .Dispose() always works but if it's faulted then there is an opportunity to clean up or something. I'm just brainstorming syntax ideas, but to manually do the following instead of using(..) {} ..

    if (this.State == CommunicationState.Closing ||
    this.State == CommunicationState.Closed ||
    this.State == CommunicationState.Faulted)
{
    this.Abort();
}
else
{
    this.Close();
}

.. is a nightmare.

@svick

This comment has been minimized.

Show comment
Hide comment
@svick

svick Mar 24, 2015

Contributor

@stimpy77 To me, that sounds as if WCF should be fixed, instead of creating a language feature to fix it from the outside.

And, how is your IAbortDisposable any better than normal IDisposable, whose implementation of Dispose() looks something like the following?

if (this.IsFaulted)
    this.Abort();
else
    this.Close();
Contributor

svick commented Mar 24, 2015

@stimpy77 To me, that sounds as if WCF should be fixed, instead of creating a language feature to fix it from the outside.

And, how is your IAbortDisposable any better than normal IDisposable, whose implementation of Dispose() looks something like the following?

if (this.IsFaulted)
    this.Abort();
else
    this.Close();
@asbjornu

This comment has been minimized.

Show comment
Hide comment
@asbjornu

asbjornu Mar 25, 2015

@stimpy77, I agree with @svick. The fact that WCF is so complex that it doesn't work with using (...) {} is not the problem of the C# language, it's the problem of WCF and should be fixed in WCF.

asbjornu commented Mar 25, 2015

@stimpy77, I agree with @svick. The fact that WCF is so complex that it doesn't work with using (...) {} is not the problem of the C# language, it's the problem of WCF and should be fixed in WCF.

@richardtallent

This comment has been minimized.

Show comment
Hide comment
@richardtallent

richardtallent Apr 3, 2015

Code contracts would be an improvement, but they treat the symptom, not the disease, as it were.

Say your method only accepts whole numbers, but uses an int parameter, which can theoretically be negative. Sure, putting a contract on the parameter to ensure it is >= 0 provides some protection, but it just moves the problem, so now the callee either has to do the same checks or defaulting before calling the method, or it has to handle the exceptions.

In the end, code contracts don't address the underlying issue -- your parameter has the wrong logical type. You're using int when you really want a far more limited type of either zero or a positive integer. Using an unsigned integer would be better, but has its own foibles, so we hold our nose and pretend that a signed type for a never-negative parameter is our best option.

So what's really going on is that your code contract is creating, logically, an anonymous type with restrictions not present in the base type. But by making it anonymous, it's not reusable, either between methods or between the method and the caller.

A better approach, IMHO, is to use the type system. E.g., if I want only whole numbers, I create a type that aliases int but does not allow negative numbers to be assigned to it:

public contract WholeInt : int where >= 0;
public contract PositiveInt : int where >= 1 ?? 1;
public contract NNString : string where !null ?? String.Empty;

public class Customer {
    public WholeInt Age { get; set; }
    public NNString Name { get; set; }
    public PositiveInt NumVisits { get; set; }
}

This moves the responsibility back where it belongs: where the invalid value assignment occurs, not when some hapless method gets the bad value as an argument. It also encourages reuse, and provides the option of an alternative default rather than an exception when a bad value is assigned or the default value of the underlying type would be invalid.

For backward compatibility, it should always be possible to implicitly cast one of these contract types to their underlying type. This would allow, for example, ICollection<T>.Count to return a WholeInt but callers can still assign the result directly to Int32.

Using the keyword contract above is just an example -- perhaps extending the concept of an Interface would be better.

richardtallent commented Apr 3, 2015

Code contracts would be an improvement, but they treat the symptom, not the disease, as it were.

Say your method only accepts whole numbers, but uses an int parameter, which can theoretically be negative. Sure, putting a contract on the parameter to ensure it is >= 0 provides some protection, but it just moves the problem, so now the callee either has to do the same checks or defaulting before calling the method, or it has to handle the exceptions.

In the end, code contracts don't address the underlying issue -- your parameter has the wrong logical type. You're using int when you really want a far more limited type of either zero or a positive integer. Using an unsigned integer would be better, but has its own foibles, so we hold our nose and pretend that a signed type for a never-negative parameter is our best option.

So what's really going on is that your code contract is creating, logically, an anonymous type with restrictions not present in the base type. But by making it anonymous, it's not reusable, either between methods or between the method and the caller.

A better approach, IMHO, is to use the type system. E.g., if I want only whole numbers, I create a type that aliases int but does not allow negative numbers to be assigned to it:

public contract WholeInt : int where >= 0;
public contract PositiveInt : int where >= 1 ?? 1;
public contract NNString : string where !null ?? String.Empty;

public class Customer {
    public WholeInt Age { get; set; }
    public NNString Name { get; set; }
    public PositiveInt NumVisits { get; set; }
}

This moves the responsibility back where it belongs: where the invalid value assignment occurs, not when some hapless method gets the bad value as an argument. It also encourages reuse, and provides the option of an alternative default rather than an exception when a bad value is assigned or the default value of the underlying type would be invalid.

For backward compatibility, it should always be possible to implicitly cast one of these contract types to their underlying type. This would allow, for example, ICollection<T>.Count to return a WholeInt but callers can still assign the result directly to Int32.

Using the keyword contract above is just an example -- perhaps extending the concept of an Interface would be better.

@adamralph

This comment has been minimized.

Show comment
Hide comment
@adamralph

adamralph Apr 3, 2015

Contributor

@richardtallent you make some very good points which should be taken note of. I guess the anti-pattern you've sniffed out there is a flavour of PrimitiveObsession.

Contributor

adamralph commented Apr 3, 2015

@richardtallent you make some very good points which should be taken note of. I guess the anti-pattern you've sniffed out there is a flavour of PrimitiveObsession.

@asbjornu

This comment has been minimized.

Show comment
Hide comment
@asbjornu

asbjornu Apr 3, 2015

Brilliant ideas, @richardtallent! I'd love to see something like that in C# one day.

asbjornu commented Apr 3, 2015

Brilliant ideas, @richardtallent! I'd love to see something like that in C# one day.

@adamralph

This comment has been minimized.

Show comment
Hide comment
@adamralph

adamralph Apr 3, 2015

Contributor

Of course, we do already have a type system which allows us to encapsulate such things 😉

The 'contracts' demonstrated above are a shorthand for value types. Whilst the syntactic sugar may drastically reduce LOC, and I welcome such improvements, we shouldn't shy away from practising this encapsulation today just because the convenient shorthand doesn't yet exist.

We should also take care not to travel only the half the distance away from anti-patterns like primitive obsession:

public contract Age: int where >= 0;

public class Customer
{
    public Age Age { get; set; }
}
Contributor

adamralph commented Apr 3, 2015

Of course, we do already have a type system which allows us to encapsulate such things 😉

The 'contracts' demonstrated above are a shorthand for value types. Whilst the syntactic sugar may drastically reduce LOC, and I welcome such improvements, we shouldn't shy away from practising this encapsulation today just because the convenient shorthand doesn't yet exist.

We should also take care not to travel only the half the distance away from anti-patterns like primitive obsession:

public contract Age: int where >= 0;

public class Customer
{
    public Age Age { get; set; }
}
@richardtallent

This comment has been minimized.

Show comment
Hide comment
@richardtallent

richardtallent Apr 4, 2015

Adam, you're right, it is a form of PrimitiveObsession. I use structs to resolve this in my own code and it works brilliantly, but syntactic sugar around to accomplish the same thing more expressively would be a welcome change.

I agree about not "traveling half the distance," but I think there should be room for allowing contracts to be composed:

public contract WholeInt : int where >= 0;
public contract PositiveInt : WholeInt where != 0;
public contract CustomerVisitCount : PositiveInt;

This is straightforward (but terribly wordy) to do with existing structs, and it's more DRY-friendly, since the same value constraints tend to pop up regularly.

I toyed around in my head the possibility of polymorphic contracts (e.g., contract PositiveInt : WholeInt, NotZeroInt) to allow even more flexible reuse, but I think I've convinced myself it would not be sufficiently better than strict composition to be worth the effort.

If Microsoft does grant us something like this, it would be great if they updated various BCL methods to use these more restrictive value types. For example, List<T>.Item could take WholeInt32 rather than plain old Int32. Maybe that would cause compatibility issues, even with implicit conversion to and from Int32. Still, it would be nice to move that direction.

richardtallent commented Apr 4, 2015

Adam, you're right, it is a form of PrimitiveObsession. I use structs to resolve this in my own code and it works brilliantly, but syntactic sugar around to accomplish the same thing more expressively would be a welcome change.

I agree about not "traveling half the distance," but I think there should be room for allowing contracts to be composed:

public contract WholeInt : int where >= 0;
public contract PositiveInt : WholeInt where != 0;
public contract CustomerVisitCount : PositiveInt;

This is straightforward (but terribly wordy) to do with existing structs, and it's more DRY-friendly, since the same value constraints tend to pop up regularly.

I toyed around in my head the possibility of polymorphic contracts (e.g., contract PositiveInt : WholeInt, NotZeroInt) to allow even more flexible reuse, but I think I've convinced myself it would not be sufficiently better than strict composition to be worth the effort.

If Microsoft does grant us something like this, it would be great if they updated various BCL methods to use these more restrictive value types. For example, List<T>.Item could take WholeInt32 rather than plain old Int32. Maybe that would cause compatibility issues, even with implicit conversion to and from Int32. Still, it would be nice to move that direction.

@MovGP0

This comment has been minimized.

Show comment
Hide comment
@MovGP0

MovGP0 Apr 22, 2015

I strongly prefer for let instead of readonly or val for readonly primitives (and types).

It is used in F#, so using let would be more consistent between different languages.

MovGP0 commented Apr 22, 2015

I strongly prefer for let instead of readonly or val for readonly primitives (and types).

It is used in F#, so using let would be more consistent between different languages.

@MovGP0

This comment has been minimized.

Show comment
Hide comment
@MovGP0

MovGP0 Apr 22, 2015

The idea of @richardtallent to define types might be a good idea. Howeven, contracts are also used ro ensure pre- and postconditions and not just type checking. So it might only be considered additionally.

MovGP0 commented Apr 22, 2015

The idea of @richardtallent to define types might be a good idea. Howeven, contracts are also used ro ensure pre- and postconditions and not just type checking. So it might only be considered additionally.

@jvlppm

This comment has been minimized.

Show comment
Hide comment
@jvlppm

jvlppm Apr 22, 2015

Why don't we have a method returning var(meaning auto)? because its not a variable?
We also have let for declarations inside linq queries.
What keyword would a method return for specifing auto?

I think this is becoming a mess.

jvlppm commented Apr 22, 2015

Why don't we have a method returning var(meaning auto)? because its not a variable?
We also have let for declarations inside linq queries.
What keyword would a method return for specifing auto?

I think this is becoming a mess.

@jvlppm

This comment has been minimized.

Show comment
Hide comment
@jvlppm

jvlppm Apr 22, 2015

Support for something like this?

class Test {
    string attribute { get; }

    public Test(int input, string test) {
        int value = input + 1; // static context.
        base(value);
        attribute = test;
    }
}

jvlppm commented Apr 22, 2015

Support for something like this?

class Test {
    string attribute { get; }

    public Test(int input, string test) {
        int value = input + 1; // static context.
        base(value);
        attribute = test;
    }
}
@MovGP0

This comment has been minimized.

Show comment
Hide comment
@MovGP0

MovGP0 Apr 22, 2015

For implementing pattern matching, I do not like to use a switch statement, since it does not return a value:

int result = match animal {
    case is Dog: 1;
    case is Cat: 2; 
    default: 0;
}

MovGP0 commented Apr 22, 2015

For implementing pattern matching, I do not like to use a switch statement, since it does not return a value:

int result = match animal {
    case is Dog: 1;
    case is Cat: 2; 
    default: 0;
}
@MovGP0

This comment has been minimized.

Show comment
Hide comment
@MovGP0

MovGP0 Apr 22, 2015

@jvlppm it would be almost pointless to have a method that returns var.

Granted, the return type could be interfered by the compiler, which would ease refactorings. but it might also increase the programmer error rate by beeing less explicit.

I believe it is very important to have a language that decreases the potential error rate by design, which is why non-nullable reference types, readonly values, contracts, pattern matching, and even units/dimensions are a good idea.

MovGP0 commented Apr 22, 2015

@jvlppm it would be almost pointless to have a method that returns var.

Granted, the return type could be interfered by the compiler, which would ease refactorings. but it might also increase the programmer error rate by beeing less explicit.

I believe it is very important to have a language that decreases the potential error rate by design, which is why non-nullable reference types, readonly values, contracts, pattern matching, and even units/dimensions are a good idea.

@MovGP0

This comment has been minimized.

Show comment
Hide comment
@MovGP0

MovGP0 Apr 22, 2015

@ggrnd0 the catch/finally block after a using block is not a very good idea, since having both in the same method violates SRP. Using an aspect to do the exception handling is a much better idea.

MovGP0 commented Apr 22, 2015

@ggrnd0 the catch/finally block after a using block is not a very good idea, since having both in the same method violates SRP. Using an aspect to do the exception handling is a much better idea.

@AdamSpeight2008

This comment has been minimized.

Show comment
Hide comment
@AdamSpeight2008

AdamSpeight2008 Apr 24, 2015

Contributor

Method Contracts should be generalized out to a traits system ( #129 ).

Contributor

AdamSpeight2008 commented Apr 24, 2015

Method Contracts should be generalized out to a traits system ( #129 ).

@MovGP0

This comment has been minimized.

Show comment
Hide comment
@MovGP0

MovGP0 Apr 24, 2015

@AdamSpeight2008 I agree that traits are a powerful concept.

However, I am unsure if that can do things like checking the order in which methods are called at compile time, as you can do with contracts.

MovGP0 commented Apr 24, 2015

@AdamSpeight2008 I agree that traits are a powerful concept.

However, I am unsure if that can do things like checking the order in which methods are called at compile time, as you can do with contracts.

@MovGP0

This comment has been minimized.

Show comment
Hide comment
@MovGP0

MovGP0 Apr 24, 2015

@richardtallent i would use the value keyword in your example:

public contract WholeInt : int where value >= 0;
public contract PositiveInt : int where value >= 1;
public contract NonEmptyString : string where value != null && value != String.Empty;

MovGP0 commented Apr 24, 2015

@richardtallent i would use the value keyword in your example:

public contract WholeInt : int where value >= 0;
public contract PositiveInt : int where value >= 1;
public contract NonEmptyString : string where value != null && value != String.Empty;
@richardtallent

This comment has been minimized.

Show comment
Hide comment
@richardtallent

richardtallent Apr 24, 2015

On Apr 24, 2015, at 12:02 AM, Johann Dirry notifications@github.com wrote:

@richardtallent i would use the value keyword in your example:

public contract WholeInt : int where value >= 0;
public contract PositiveInt : int where value >= 1;
public contract NonEmptyString : string where value != null && value != String.Empty;

Excellent idea. It also makes it easier to support conditions that need dotted notation, like the following:

public contract QuadrantIPoint : Point where value.X >=0 && value.Y >= 0;

public contract SSN : string where (value != null) && (value.Length==9) && value.All(c => c >= '0' && c <= '9');

Regarding your comment earlier about pre/post conditions, I agree. This concept would handle many common pre/post conditions, but since the contract is defined outside the method taking or returning it, it would have limited visibility and would not be able to, for example, compare its value to other arguments of the method. So it's not a complete replacement for the functionality of the current contract system, just a way to cleanly deal with classes of contracts that really just boil down to a more constrained version of another type.

--Richard

richardtallent commented Apr 24, 2015

On Apr 24, 2015, at 12:02 AM, Johann Dirry notifications@github.com wrote:

@richardtallent i would use the value keyword in your example:

public contract WholeInt : int where value >= 0;
public contract PositiveInt : int where value >= 1;
public contract NonEmptyString : string where value != null && value != String.Empty;

Excellent idea. It also makes it easier to support conditions that need dotted notation, like the following:

public contract QuadrantIPoint : Point where value.X >=0 && value.Y >= 0;

public contract SSN : string where (value != null) && (value.Length==9) && value.All(c => c >= '0' && c <= '9');

Regarding your comment earlier about pre/post conditions, I agree. This concept would handle many common pre/post conditions, but since the contract is defined outside the method taking or returning it, it would have limited visibility and would not be able to, for example, compare its value to other arguments of the method. So it's not a complete replacement for the functionality of the current contract system, just a way to cleanly deal with classes of contracts that really just boil down to a more constrained version of another type.

--Richard

@AlexeyRaga

This comment has been minimized.

Show comment
Hide comment
@AlexeyRaga

AlexeyRaga Apr 28, 2015

Abstraction over generics, anyone +1?

I mean, I can abstract over types with generics, e.g. List<T>, but for some reason I cannot abstract one level above e.g. MyKind<F<_>> where MyKind takes not a proper type, but a generic as a parameter which you would construct like, say, MyKind<List> or MyKind<Task>

This would be very and opens soooo many doors (in functional programming direction mainly, but not only). For example, it would finally be possible to generalise over IEnumerable<T> and IObservable<T> and to have functionality that can accept either of them.

Or it gives us the ability to write functions that preserve shape, like "you give me a list - I return you a list, you give me a task - I return you a task, you give me an observable - I return you an observable".

AlexeyRaga commented Apr 28, 2015

Abstraction over generics, anyone +1?

I mean, I can abstract over types with generics, e.g. List<T>, but for some reason I cannot abstract one level above e.g. MyKind<F<_>> where MyKind takes not a proper type, but a generic as a parameter which you would construct like, say, MyKind<List> or MyKind<Task>

This would be very and opens soooo many doors (in functional programming direction mainly, but not only). For example, it would finally be possible to generalise over IEnumerable<T> and IObservable<T> and to have functionality that can accept either of them.

Or it gives us the ability to write functions that preserve shape, like "you give me a list - I return you a list, you give me a task - I return you a task, you give me an observable - I return you an observable".

@AlexeyRaga

This comment has been minimized.

Show comment
Hide comment
@AlexeyRaga

AlexeyRaga Apr 28, 2015

Sum types!
Someone has mentioned discriminated unions, from my POV it is much bigger than any contracts, especially if we have pattern matching.

AlexeyRaga commented Apr 28, 2015

Sum types!
Someone has mentioned discriminated unions, from my POV it is much bigger than any contracts, especially if we have pattern matching.

@AlenPelin

This comment has been minimized.

Show comment
Hide comment
@AlenPelin

AlenPelin Aug 29, 2015

Apologize if I'm writing to the wrong thread, please direct me if I should post the idea somewhere else or open separate issue for that.

From my experience I noticed same typical situation that I write over and over:

foreach (var languageVersions in languageData)
{
  var language = languageVersions.Key;
  var versions = languageVersions.Value;
  foreach (var versionFields in versions)
  {
    var versionNumber = versionFields.Key;
    var fields = versionFields.Value;
    Process(language, versionNumber, fields);
  }
}

It would be really handy if there is special support of IDictionary<TKey, TValue> in C# that can simplify the code and make it more expressive. Something like that:

foreach (var language, versions in languageData)
{
  foreach (var versionNumber, fields in versions)
  {
    Process(language, versionNumber, fields);
  }
}

Does anybody think it is a good idea?

AlenPelin commented Aug 29, 2015

Apologize if I'm writing to the wrong thread, please direct me if I should post the idea somewhere else or open separate issue for that.

From my experience I noticed same typical situation that I write over and over:

foreach (var languageVersions in languageData)
{
  var language = languageVersions.Key;
  var versions = languageVersions.Value;
  foreach (var versionFields in versions)
  {
    var versionNumber = versionFields.Key;
    var fields = versionFields.Value;
    Process(language, versionNumber, fields);
  }
}

It would be really handy if there is special support of IDictionary<TKey, TValue> in C# that can simplify the code and make it more expressive. Something like that:

foreach (var language, versions in languageData)
{
  foreach (var versionNumber, fields in versions)
  {
    Process(language, versionNumber, fields);
  }
}

Does anybody think it is a good idea?

@alrz

This comment has been minimized.

Show comment
Hide comment
@alrz

alrz Aug 29, 2015

Contributor

@AlenPelin if KeyValuePair<,> could be treated as a tuple, since Dictionary<,> implements IEnumerable<> you should get this out-of-the-box.

foreach ((var language, var versions) in languageData)
{
  foreach ((var versionNumber, var fields) in versions)
  {
    Process(language, versionNumber, fields);
  }
}
Contributor

alrz commented Aug 29, 2015

@AlenPelin if KeyValuePair<,> could be treated as a tuple, since Dictionary<,> implements IEnumerable<> you should get this out-of-the-box.

foreach ((var language, var versions) in languageData)
{
  foreach ((var versionNumber, var fields) in versions)
  {
    Process(language, versionNumber, fields);
  }
}
@AlenPelin

This comment has been minimized.

Show comment
Hide comment
@AlenPelin

AlenPelin Aug 29, 2015

@alrz thanks, I will check that.
UPD: I reviewed the tuple concept and you seem to be right, just need to wait for tuples to be implemented first ^_^ which is quite complicated thought.

AlenPelin commented Aug 29, 2015

@alrz thanks, I will check that.
UPD: I reviewed the tuple concept and you seem to be right, just need to wait for tuples to be implemented first ^_^ which is quite complicated thought.

@gafter

This comment has been minimized.

Show comment
Hide comment
@gafter

gafter Aug 31, 2015

Member

@alrz The foreach loop does not specify that it does decomposition, so even if we add a decomposition operator/assignment to the language, you would not get any special support in foreach without additional language support. Specifically, the syntax you wrote provided a type for the iteration variable (var language, var versions) but then you omitted the name of the variable - thus it would be a syntax error.

Member

gafter commented Aug 31, 2015

@alrz The foreach loop does not specify that it does decomposition, so even if we add a decomposition operator/assignment to the language, you would not get any special support in foreach without additional language support. Specifically, the syntax you wrote provided a type for the iteration variable (var language, var versions) but then you omitted the name of the variable - thus it would be a syntax error.

@AlenPelin

This comment has been minimized.

Show comment
Hide comment
@AlenPelin

AlenPelin Aug 31, 2015

Thanks @gafter for your comments.
Well, as soon as foreach needs special support for either KeyValuePair, or tuples and KeyValuePair needs tuple representation, I believe first option is much easier to implement. Anyway, the question is still open: whether we really need that sugar or not.

AlenPelin commented Aug 31, 2015

Thanks @gafter for your comments.
Well, as soon as foreach needs special support for either KeyValuePair, or tuples and KeyValuePair needs tuple representation, I believe first option is much easier to implement. Anyway, the question is still open: whether we really need that sugar or not.

@alrz

This comment has been minimized.

Show comment
Hide comment
@alrz

alrz Aug 31, 2015

Contributor

@gafter of course currently it is a syntax error! this is based on my assumption — that pattern matching, tuples, record types, etc are a group of features (in a functional language though) that always come together, otherwise it doesn't make much sense. If we have special syntax for them it should support decomposition everywhere not just in assignments. including

// switch statement
void F(object o) {
    switch(o) {
    case (int item1, int item2): ... 
    }
}

// method parameters
void F((int item1, int item2)) { ... }

and aforementioned scenarios — which are supported in F# plus a powerful type inference, you don't even need to mention types as I did here.

and I didn't quite get what you meant by

you omitted the name of the variable

of course I did because I'm not intrested in the tuple itself, but just the members. However, in Haskell you can have both at the same time with as-patterns e.g. whole_tuple@(item1,item2) hence it wouldn't be an unreasonable feature.

Contributor

alrz commented Aug 31, 2015

@gafter of course currently it is a syntax error! this is based on my assumption — that pattern matching, tuples, record types, etc are a group of features (in a functional language though) that always come together, otherwise it doesn't make much sense. If we have special syntax for them it should support decomposition everywhere not just in assignments. including

// switch statement
void F(object o) {
    switch(o) {
    case (int item1, int item2): ... 
    }
}

// method parameters
void F((int item1, int item2)) { ... }

and aforementioned scenarios — which are supported in F# plus a powerful type inference, you don't even need to mention types as I did here.

and I didn't quite get what you meant by

you omitted the name of the variable

of course I did because I'm not intrested in the tuple itself, but just the members. However, in Haskell you can have both at the same time with as-patterns e.g. whole_tuple@(item1,item2) hence it wouldn't be an unreasonable feature.

@gafter

This comment has been minimized.

Show comment
Hide comment
@gafter

gafter Sep 1, 2015

Member

@alrz We already have one set of method overload resolution/dispatch rules. It is too late to replace them with a set that would have made sense if we had started with pattern matching. For example, if we were to do pattern matching for method dispatch as many functional languages do, we would select the overload based on the dynamic type of parameters rather than just upon the static type. Since that would change the behavior of existing code (int x is syntactically a pattern and also a parameter declaration), we cannot compatibly make that change.

Member

gafter commented Sep 1, 2015

@alrz We already have one set of method overload resolution/dispatch rules. It is too late to replace them with a set that would have made sense if we had started with pattern matching. For example, if we were to do pattern matching for method dispatch as many functional languages do, we would select the overload based on the dynamic type of parameters rather than just upon the static type. Since that would change the behavior of existing code (int x is syntactically a pattern and also a parameter declaration), we cannot compatibly make that change.

@gafter

This comment has been minimized.

Show comment
Hide comment
@gafter

gafter Apr 25, 2016

Member

Design notes have been archived at https://github.com/dotnet/roslyn/blob/future/docs/designNotes/2015-01-21%20C%23%20Design%20Meeting.md but discussion can continue here.

Member

gafter commented Apr 25, 2016

Design notes have been archived at https://github.com/dotnet/roslyn/blob/future/docs/designNotes/2015-01-21%20C%23%20Design%20Meeting.md but discussion can continue here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment