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

Comments

Projects
None yet
5 participants
@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:

  • As the second operand of a ternary conditional operator ?:
  • As the third operand of a ternary conditional operator ?:
  • As the second operand of a null coalescing operator ??
  • After the colon of a match section (see #5154)
  • As the body of an expression-bodied lambda or method.

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

@gafter gafter added the Language-C# label Sep 10, 2015

@aluanhaddad

This comment has been minimized.

Copy link

aluanhaddad commented Sep 11, 2015

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

This comment has been minimized.

Copy link

olmobrutall commented Oct 9, 2015

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

This comment has been minimized.

Copy link
Member

gafter commented Oct 9, 2015

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

@olmobrutall

This comment has been minimized.

Copy link

olmobrutall commented Oct 9, 2015

@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

This comment has been minimized.

Copy link
Member

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

This comment has been minimized.

Copy link

olmobrutall commented Oct 10, 2015

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

@olmobrutall

This comment has been minimized.

Copy link

olmobrutall commented Oct 10, 2015

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

This comment has been minimized.

Copy link

stepanbenes commented Oct 21, 2015

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

@stepanbenes

This comment has been minimized.

Copy link

stepanbenes commented Oct 21, 2015

@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

This comment has been minimized.

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

This comment has been minimized.

Copy link

stepanbenes commented Oct 21, 2015

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

@stepanbenes

This comment has been minimized.

Copy link

stepanbenes commented Oct 21, 2015

@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 referenced this issue Oct 27, 2015

Merged

Pattern Matching #4882

31 of 49 tasks complete

@gafter gafter changed the title [Proposal] Make "throw expression" an expression form Proposal: Make "throw expression" an expression form Oct 28, 2015

@gafter

This comment has been minimized.

Copy link
Member

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

Move pattern-related parsing into a separate source file.
Implemented the match expression. dotnet#5154
Implemented the throw expression. dotnet#5143
@gafter

This comment has been minimized.

Copy link
Member

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.

@alrz

This comment has been minimized.

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

This comment has been minimized.

Copy link
Member

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

This comment has been minimized.

Copy link

olmobrutall commented Nov 24, 2015

Speaking of LINQ, maybe the new throw expression should be emitted as an expression tree as well: #2060

@alrz

This comment has been minimized.

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

This comment has been minimized.

Copy link
Member

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