{"action":"created","issue":{"url":"https://api.github.com/repos/dotnet/roslyn/issues/11308","repository_url":"https://api.github.com/repos/dotnet/roslyn","labels_url":"https://api.github.com/repos/dotnet/roslyn/issues/11308/labels{/name}","comments_url":"https://api.github.com/repos/dotnet/roslyn/issues/11308/comments","events_url":"https://api.github.com/repos/dotnet/roslyn/issues/11308/events","html_url":"https://github.com/dotnet/roslyn/issues/11308","id":154844998,"number":11308,"title":"Proposal: \"sync\" block in async/iterator methods","user":{"login":"svick","id":287848,"avatar_url":"https://avatars.githubusercontent.com/u/287848?v=3","gravatar_id":"","url":"https://api.github.com/users/svick","html_url":"https://github.com/svick","followers_url":"https://api.github.com/users/svick/followers","following_url":"https://api.github.com/users/svick/following{/other_user}","gists_url":"https://api.github.com/users/svick/gists{/gist_id}","starred_url":"https://api.github.com/users/svick/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/svick/subscriptions","organizations_url":"https://api.github.com/users/svick/orgs","repos_url":"https://api.github.com/users/svick/repos","events_url":"https://api.github.com/users/svick/events{/privacy}","received_events_url":"https://api.github.com/users/svick/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/dotnet/roslyn/labels/Area-Language%20Design","name":"Area-Language Design","color":"5319e7"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":10,"created_at":"2016-05-14T09:56:55Z","updated_at":"2016-06-02T23:41:02Z","closed_at":null,"body":"A relatively common request is to do something in a state machine method (i.e. `async` method or iterator method) before the state machine is created, either for efficiency (common with `async`) or for correctness (common with iterators).\r\n\r\nIn C# 7, these cases can be solved fairly well with local functions (the second example uses *arbitrary async returns* #10902):\r\n\r\n```c#\r\npublic static IEnumerable<T> Where<T>(this IEnumerable<T> source, Func<T, bool> predicate)\r\n{\r\n    if (source == null || predicate == null)\r\n        throw new ArgumentNullException();\r\n\r\n    IEnumerable<T> WhereImpl()\r\n    {\r\n        foreach (var item in source)\r\n        {\r\n            if (predicate(item))\r\n                yield return item;\r\n        }\r\n    }\r\n\r\n    return WhereImpl();\r\n}\r\n\r\npublic static ValueTask<int> DelayWithResult(int delay)\r\n{\r\n    if (delay == 0)\r\n        return ValueTask<int>.FromValue(delay);\r\n\r\n    async ValueTask<int> DelayImpl()\r\n    {\r\n        await Task.Delay(delay);\r\n        return delay;\r\n    }\r\n\r\n    return DelayImpl();\r\n}\r\n```\r\n\r\nMy proposal is to make this even nicer, by including a `sync` block at the start of the method that contains the code that should execute before the state machine is created. Also, if the `sync` block returns or throws, the result is returned or thrown directly, without wrapping in `Task`, tasklike, or `IEnumerable<T>`.\r\n\r\nThis means the examples above become:\r\n\r\n```c#\r\npublic static IEnumerable<T> Where(this IEnumerable<T> source, Func<T, bool> predicate)\r\n{\r\n    sync\r\n    {\r\n        if (source == null || predicate == null)\r\n            throw new ArgumentNullException();\r\n    }\r\n\r\n    foreach (var item in source)\r\n    {\r\n        if (predicate(item))\r\n            yield return item;\r\n    }\r\n}\r\n\r\npublic static async ValueTask<int> DelayWithResult(int delay)\r\n{\r\n    sync\r\n    {\r\n        if (delay == 0)\r\n            return ValueTask<int>.FromValue(delay);\r\n    }\r\n\r\n    await Task.Delay(delay);\r\n    return delay;\r\n}\r\n```\r\n\r\nThis is similar to *Defer async state machine creation* #10449, except that it's explicit. Being explicit has both advantages (the change of semantics regarding what happens to thrown exceptions is allowed) and disadvantages (the optimization doesn't happen automatically).\r\n\r\nThe keyword `sync` is not ideal: it's not currently a keyword and could be confusing for iterator methods. Suggestions for alternative syntax are welcome."},"comment":{"url":"https://api.github.com/repos/dotnet/roslyn/issues/comments/223454433","html_url":"https://github.com/dotnet/roslyn/issues/11308#issuecomment-223454433","issue_url":"https://api.github.com/repos/dotnet/roslyn/issues/11308","id":223454433,"user":{"login":"playsomethingsaxman","id":17526748,"avatar_url":"https://avatars.githubusercontent.com/u/17526748?v=3","gravatar_id":"","url":"https://api.github.com/users/playsomethingsaxman","html_url":"https://github.com/playsomethingsaxman","followers_url":"https://api.github.com/users/playsomethingsaxman/followers","following_url":"https://api.github.com/users/playsomethingsaxman/following{/other_user}","gists_url":"https://api.github.com/users/playsomethingsaxman/gists{/gist_id}","starred_url":"https://api.github.com/users/playsomethingsaxman/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/playsomethingsaxman/subscriptions","organizations_url":"https://api.github.com/users/playsomethingsaxman/orgs","repos_url":"https://api.github.com/users/playsomethingsaxman/repos","events_url":"https://api.github.com/users/playsomethingsaxman/events{/privacy}","received_events_url":"https://api.github.com/users/playsomethingsaxman/received_events","type":"User","site_admin":false},"created_at":"2016-06-02T23:41:02Z","updated_at":"2016-06-02T23:41:02Z","body":"The constructor isn't to be awaited, if you need that then there's the static factory method pattern. It simply continues initialization without having to call a separate dangling async method that also has the danger of being called again by other class members.\r\n\r\nI've built epicly scaleable code with constructs like SwitchTo() because intent just flows so much better and faster. Everyone else is just shooting themselves in the foot with dogma. You can't crank serious code out with Task.Run(). And the .NET Framework thread pool is pure crap anyway, no offense. I've seen it crack and shatter embarrassingly with my butt on the line. It doesn't scale to anything, and people get around it by throwing more servers at the problem. I think that's the real reason for .NET Core, to secretly rewrite the crap, I don't think anyone says it out loud, lol"}}
{"actor":{"display_login":"playsomethingsaxman"}}