How does C# async/await relates to more general constructs, e.g. F# workflows or monads?

前端 未结 2 1123
长情又很酷
长情又很酷 2021-01-30 05:19

The C# language design have always (historically) been geared towards solving specific problems rather then finding to address the underlying general problems: see for example h

2条回答
  •  遇见更好的自我
    2021-01-30 05:49

    Tomas's answer is very good. To add a few more things:

    The C# language design have always (historically) been geared towards solving specific problems rather then finding to address the underlying general problems

    Though there is some truth to that, I don't think it is an entirely fair or accurate characterization, so I'm going to start my answer by denying the premise of your question.

    It is certainly true that there is a spectrum with "very specific" on one end and "very general" on the other, and that solutions to specific problems fall on that spectrum. C# is designed as a whole to be a highly general solution to a great many specific problems; that's what a general-purpose programming language is. You can use C# to write everything from web services to XBOX 360 games.

    Since C# is designed to be a general-purpose programming language, when the design team identifies a specific user problem they always consider the more general case. LINQ is an excellent case in point. In the very early days of the design of LINQ, it was little more than a way to put SQL statements in a C# program, because that's the problem space that was identified. But quite soon in the design process the team realized that the concepts of sorting, filtering, grouping and joining data applied not just to tabular data in a relational database, but also hierarchical data in XML, and to ad-hoc objects in memory. And so they decided to go for the far more general solution that we have today.

    The trick of design is figuring out where on the spectrum it makes sense to stop. The design team could have said, well, the query comprehension problem is actually just a specific case of the more general problem of binding monads. And the binding monads problem is actually just a specific case of the more general problem of defining operations on higher kinds of types. And surely there is some abstraction over type systems... and enough is enough. By the time we get to solving the bind-an-arbitrary-monad problem, the solution is now so general that the line-of-business SQL programmers who were the motivation for the feature in the first place are completely lost, and we haven't actually solved their problem.

    The really major features added since C# 1.0 -- generic types, anonymous functions, iterator blocks, LINQ, dynamic, async -- all have the property that they are highly general features useful in many different domains. They can all be treated as specific examples of a more general problem, but that is true of any solution to any problem; you can always make it more general. The idea of the design of each of these features is to find the point where they can't be made more general without confusing their users.

    Now that I've denied the premise of your question, let's look at the actual question:

    What I am trying to understand is to which "general construct" the async/await keywords relate to

    It depends on how you look at it.

    The async-await feature is built around the Task type, which is as you note, a monad. And of course if you talked about this with Erik Meijer he would immediately point out that Task is actually a comonad; you can get the T value back out the other end.

    Another way to look at the feature is to take the paragraph you quoted about iterator blocks and substitute "async" for "iterator". Asynchronous methods are, like iterator methods, a kind of coroutine. You can think of Task as just an implementation detail of the coroutine mechanism if you like.

    A third way to look at the feature is to say that it is a kind of call-with-current-continuation (commonly abbreviated call/cc). It is not a complete implementation of call/cc because it does not take the state of the call stack at the time that the continuation is signed up into account. See this question for details:

    How could the new async feature in c# 5.0 be implemented with call/cc?

    I will wait and see if someone (Eric? Jon? maybe you?) can fill in more details on how actually C# generates code to implement await,

    The rewrite is essentially just a variation on how iterator blocks are rewritten. Mads goes through all the details in his MSDN Magazine article:

    http://msdn.microsoft.com/en-us/magazine/hh456403.aspx

提交回复
热议问题