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

Support for Block Bodied Switch Expression Branches #2636

Closed
TheFanatr opened this issue Jul 7, 2019 · 10 comments
Closed

Support for Block Bodied Switch Expression Branches #2636

TheFanatr opened this issue Jul 7, 2019 · 10 comments

Comments

@TheFanatr
Copy link

TheFanatr commented Jul 7, 2019

This proposal asks for the ability to define a block body for individual switch expression branches. I believe this should be a fairly straightforward addition, considering that there is no other obvious way to execute multiple statements in a branch, minus a few roundabout ways such as defining a local function and calling it or instantiating a Func<T> that is invoked immediately.

Syntax Proposal

Note that example code is trivial.

bool stop = false;
start:
string description = Console.ReadLine() switch
{
    "" => "Empty",
    "/stop" =>
    {
        stop = true;
        return "A Process Stop Command";
    },
    "/reset" =>
    {
        Descriptions = new List<(string, string)> { };
        return "Interpreter Reset Request";
    },
    var input => 
    {
        var result = "";
        foreach ((string describedInput, string description) in Descriptions)
        {
            if (input.Equals(describedInput))
                return description;
        }
        Console.WriteLine("Text Unrecognized; Please Input a Description: ");
        var newDescription = Console.ReadLine();
        Descriptions.Add((input, newDescription));
        return newDescription;
    },
    _ => "Errored"
};
AddToLogFile($"Input was successfully described as {description}.");
Console.WriteLine($"Input Description: {description}");
if (!stop)
    goto start;

Instead of return, in order to clarify that the statement does not terminate control flow for the member, a new keyword could be introduced to represent the submission of a result from a branch such as submit.

Implementation

A PoC could probably simply implement this by instantiating a Func<T> via a rewrite, but I am unsure as to how feasible it would be to do. Eventually, I don't see why this couldn't just be evaluated like any other block body such as one denoted via { } in a member body, with the return simply submitting the result for the branch as it is currently done. This may be easier said than done.

What are your thoughts?

@HaloFour
Copy link
Contributor

HaloFour commented Jul 7, 2019

A return statement within a block is already legal syntax in C# and it returns from the method, not the block.

#377 and #973 has been proposed (and the latter championed) as a way to declare an expression as a sequence of statements.

@Thaina
Copy link

Thaina commented Jul 8, 2019

#249 (comment)

@TheFanatr
Copy link
Author

A return statement within a block is already legal syntax in C# and it returns from the method, not the block.

I agree, but I suggested it as the default anyways because lambda expressions have optional block bodies and the use of the return keyword in those is to denote control flow termination within the expression, not the enclosing body, so it wouldn't be that much of a stretch; what I was saying was that in this case it should not be treated as a regular scoping block, but rather more similar to how a lambda expression is treated.

@TheFanatr
Copy link
Author

TheFanatr commented Jul 8, 2019

I provided submit as a suggested alternative to denote the termination of control flow in a body within an expression. The yield keyword could also be used, but it may actually conflict with expectation more than the return keyword would.

@TheFanatr
Copy link
Author

Also @HaloFour, I'm not sure what relevancy the declaration expressions proposal (#973) has with this one; could you please clarify?

@DavidArno
Copy link

I personally, strongly dislike this proposal and I think your example code highlights why I don't like it. What ought to be an expression is suddenly turned into reams of procedural code. Using sequence expressions, as @HaloFour highlights, we could write it as:

string description = Console.ReadLine() switch {
    "" => "Empty",
    "/stop" => (stop = true, "A Process Stop Command"),
    "/reset" => (Descriptions = new List<(string, string)> { }, "Interpreter Reset Request"),
    var input => ProcessInput(input),
    _ => "Errored"
);
AddToLogFile($"Input was successfully described as {description}.");
Console.WriteLine($"Input Description: {description}");

string ProcessInput(string input)
{
    var result = "";
    foreach ((string describedInput, string description) in Descriptions)
    {
        if (input.Equals(describedInput)) return description;
    }
    Console.WriteLine("Text Unrecognized; Please Input a Description: ");
    var newDescription = Console.ReadLine();
    Descriptions.Add((input, newDescription));
    return newDescription;
}

but even that looks ugly in my view, with those mutating variables spread throughout the expression, but that then becomes a concern over sequence expressions, which is a different topic.

@TheFanatr
Copy link
Author

While I agree that having some sort of way to chain expressions in the way you have demoed would be nice, what I am suggesting is that certain syntax be implemented where the definition of a new local method, as you have done, would not be necessary to simply execute a body of statements in a switch expression branch; what I have found in the past, as is obviated with even your example, is that the abuse of other code constructs is needed to work around the fact that it is not currently possible to have a block body in a switch expression branch.

@TheFanatr
Copy link
Author

TheFanatr commented Jul 9, 2019

I have also thought of a new syntax that could be used where an expression body is attached to the end of the block.

string description = Console.ReadLine() switch
{
    "" => "Empty",
    "/stop" => stop = true => "A Process Stop Command",
    "/reset" => Descriptions = new List<(string, string)> { } => "Interpreter Reset Request",
    var input => 
    {
        var result = "";
        foreach ((string describedInput, string description) in Descriptions)
        {
            if (input.Equals(describedInput))
                return description;
        }
        Console.WriteLine("Text Unrecognized; Please Input a Description: ");
        var newDescription = Console.ReadLine();
        Descriptions.Add((input, newDescription));
    } => newDescription,
    _ => "Errored"
}

@YairHalberstadt
Copy link
Contributor

Closing as championed at #3038

@333fred
Copy link
Member

333fred commented Nov 1, 2020

Actually championed at #3037, not 3038

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

No branches or pull requests

6 participants