Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Make "throw expression" an expression form #5143

Closed
gafter opened this issue Sep 10, 2015 · 19 comments
Closed

Proposal: Make "throw expression" an expression form #5143

gafter opened this issue Sep 10, 2015 · 19 comments

Comments

@gafter
Copy link
Member

gafter commented Sep 10, 2015

The spec has been moved

The spec for the proposed throw expression has been moved to https://github.com/dotnet/roslyn/blob/future/docs/features/patterns.md

Below is a snapshot that may be out of date.


I propose to extend the set of expression forms to include

throw-expression:
      throw null-coalescing-expression
null-coalescing-expression:
      throw-expression

The type rules are as follows:

A throw-expression has no type.
A throw-expression is convertible to every type by an implicit conversion.

The flow-analysis rules are as follows:

For every variable v,

  • v is definitely assigned before the null-coalescing-expression of a throw-expression iff it is definitely assigned before the throw-expression.
  • v is definitely assigned after throw-expression.

A throw expression is allowed in only the following contexts:


This proposal is intended to facilitate a move toward expression-oriented programming, adding convenience in a number of scenarios. For example

An expression-bodied method may now throw

void M() => throw new NotImplementedException();

A conditional expression may throw on one branch:

var foo = term.HasValue ? term.Value.Foo() : throw new ArgumentException("term");

I am proposing this to facilitate a proposal (#5154) for an expression form of a pattern-matching switch, where one may want to throw in some branch.

This is related to #59 and #1226.

@gafter gafter added this to the C# 7 and VB 15 milestone Sep 10, 2015
@gafter gafter self-assigned this Sep 10, 2015
@aluanhaddad
Copy link

This is a wonderful idea. It is simple, it paves the way for future constructs. It will improve many existing language features, such as lambda expressions, and by extension that will improve the elegance of certain patterns, such as dispatch dictionaries.

@olmobrutall
Copy link

I like this solution, but It can be implemented today with this nice trick:

public static T Throw<T>(this Exception ex)
{
     throw ex;
}

And used like:

var foo = term.HasValue ? DateTime.Now : new ArgumentException("term").Throw<DateTime>();

Just my 2 cents...

@gafter
Copy link
Member Author

gafter commented Oct 9, 2015

@olmobrutall That does not have the correct definite-assignment behavior.

@olmobrutall
Copy link

@gafter Can you elaborate on that? I need an example to understand the following sentence

v is definitely assigned before the expression of a throw-expression iff it is definitely assigned before the throw-expression.

Still, just for the sake of discoverability, I like your proposal very much.

@gafter
Copy link
Member Author

gafter commented Oct 10, 2015

Yes, what that means is that in an expression of the form throw e any variables that are definitely assigned before throw e are definitely assigned before e. The example is

Exception x = new Exception();
var q = condition ? 2 : throw x; // is x definitely assigned here?

We need the quoted rule to answer that question.

@olmobrutall
Copy link

Mmmm... Then what's the problem with the Throw<T> definitely assignment?

@olmobrutall
Copy link

Oh, I know. You mean for the trivial case.

This won't compile because no value is returned:

public int Foo(){
string s = new Exception().Throw<string>();
}

In general this is not a problem because you use it in conditional expressions but this is another point to implement your proposal.

@stepanbenes
Copy link

@alrz I don't get it. What would be the type of returned object?

@stepanbenes
Copy link

@alrz So, you want to change the behavior of return keyword to throw the exceptions? Otherwise, what would be the value of the never object?

@alrz
Copy link
Contributor

alrz commented Oct 21, 2015

@stepanbenes Sorry I made a mistake, never means that method doesn't return. But I think this should be possible.

@stepanbenes
Copy link

@alrz So, the return expression indicates that the method does not return? I am confused.

@stepanbenes
Copy link

@alrz The problem is that some value has to be assigned to variable t in your example, unlike with throw expression... But what value?

@gafter gafter mentioned this issue Oct 27, 2015
49 tasks
@gafter gafter changed the title [Proposal] Make "throw expression" an expression form Proposal: Make "throw expression" an expression form Oct 28, 2015
@gafter
Copy link
Member Author

gafter commented Nov 1, 2015

@olmobrutall

Mmmm... Then what's the problem with the Throw definitely assignment?

With the proposed construct:

int n;
M(condition ? (n = 12) : throw X,
    n); // ok, n is definitely assigned here

but with Throw<T>:

int n;
M(condition ? (n = 12) : Throw<int>(X),
    n); // oops, n is not definitely assigned here

This is the second bullet about definite assignment. Stated in isolation, the rule is

For every variable v, v is definitely assigned after throw-expression.

gafter added a commit to gafter/roslyn that referenced this issue Nov 1, 2015
Implemented the match expression. dotnet#5154
Implemented the throw expression. dotnet#5143
@gafter
Copy link
Member Author

gafter commented Nov 4, 2015

All: A draft spec, roughly up-to-date, for pattern-matching is now in review #6570 and includes this feature.

@gafter gafter removed this from the C# 7 and VB 15 milestone Nov 20, 2015
@gafter gafter added the 4 - In Review A fix for the issue is submitted for review. label Nov 20, 2015
@alrz
Copy link
Contributor

alrz commented Nov 24, 2015

shouldn't continue, break and return also be an expression so that the following would be possible?

do {
  let result = F() switch(
    case Some(value): value,
    case None(): continue
  );
} while( ... );

I know this is possible with let .. else but what if there were multiple cases?

Then this one would be a viable option (combining #1938 and #6877)

from item in list
let Some(value) = item else continue
do WriteLine(value);

// equivalent to

foreach(var item in list) {
  let Some(value) = item else continue;
  WriteLine(value);
} 

@gafter
Copy link
Member Author

gafter commented Nov 24, 2015

@alrz The short answer is "no". throw is not a control statement in the same sense as continue and break, and they make less sense in an expression context.

Linq queries are not, in general, equivalent to loops unless they happen to use MS's System.Linq extension methods on IEnumerable.

@olmobrutall
Copy link

Speaking of LINQ, maybe the new throw expression should be emitted as an expression tree as well: https://github.com/dotnet/roslyn/issues/2060

@alrz
Copy link
Contributor

alrz commented Jan 5, 2016

@gafter In Rust that would be possible,

        let guess: u32 = match guess.trim().parse() {
            Ok(num) => num,
            Err(_) => continue,  // continue expression
        };

        match guess.cmp(&secret_number) {
            Ordering::Less    => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal   => {
                println!("You win!");
                break;
            }
        }

@gafter
Copy link
Member Author

gafter commented Jan 28, 2016

This has been folded into the pattern matching spec.

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

No branches or pull requests

5 participants