Are first-class continuations useful in modern object-oriented programming languages?

Continuations are extremely useful in functional programming languages (e.g. the Cont monad in Haskell) because they allow a simple and regular notation for imperative-style code. They’re also useful in some older imperative languages because they can be used to implement missing language features (e.g. exceptions, coroutines, green threads). But for a modern object-oriented language with support built in for these features, what arguments would there be for also adding support for first-class continuations (whether the more modern delimited style reset and shift or scheme-like call-with-current-continuation)?

Are there arguments against adding support other than performance and complexity of implementation?

1

Let’s let Eric Lippert answer this one:

The obvious question at this point is: if CPS is so awesome then why
don’t we use it all the time? Why have most professional developers
never heard of it, or, those who have, think of it as something only
those crazy Scheme programmers do?

First of all, it is simply hard for most people who are used to
thinking about subroutines, loops, try-catch-finally and so on to
reason about delegates being used for control flow in this way. I am
reviewing my notes on CPS from CS442 right now and I see that in 1995
I wrote down a profQUOTE: “With continuations you have to sort of
stand on your head and pull yourself inside out”. Professor Duggan (*)
was absolutely correct in saying that. Recall from a couple days ago
that our tiny little example of CPS transformation of the C#
expression M(B()?C():D()) involved four lambdas. Not everyone is good
at reading code that uses higher-order functions. It imposes a large
cognitive burden.

Moreover: one of the nice things about having specific control flow
statements baked in to a language is that they let your code clearly
express the meaning of the control flow while hiding the mechanisms –
the call stacks and return addresses and exception handler lists and
protected regions and so on. Continuations make the mechanisms of
control flow explicit in the structure of the code.
All that emphasis
on mechanism can overwhelm the meaning of the code.

In the next article, he explains how asynchrony and continuations are exactly equivalent to one another, and goes over a demonstration of taking a simple, (but blocking,) synchronous network operation and rewriting it in asyncronous style, making sure to cover all the hidden gotchas that have to be covered in order to get it right. It turns into a massive mess of code. His summary at the end:

Holy goodness, what a godawful mess we’ve made. We’ve expanded two
lines of perfectly clear code into two dozen lines of the most
godawful spaghetti you’ve ever seen. And of course it still doesn’t
even compile because the labels aren’t in scope and we have a definite
assignment error. We;d still need to further rewrite the code to fix
those problems.

Remember what I was saying about the pros and cons of CPS?

  • PRO: Arbitrarily complex and interesting control flows can be built out of simple parts – check.
  • CON: The reification of control flow via continuations is hard to read and hard to reason about – check.
  • CON: The code that represents the mechanisms of control flow completely overwhelms the meaning of the code – check.
  • CON: The transformation of ordinary code control flow into CPS is the kind of thing that compilers are good at, and almost no one else
    is – check.

This is not some intellectual exercise. Real people end up writing
code morally equivalent to the above all the time when they deal with
asynchrony.
And, ironically, even as processors have gotten faster and
cheaper, we spend more and more of our time waiting for stuff that
isn’t processor-bound.
In many programs, much of the time spent is
with the processor pegged to zero waiting for network packets to make
the trip from England to Japan and back again, or for disks to spin,
or whatever.

And I haven’t even talked about what happens if you want to compose
asynchronous operations further. Suppose you want to make
ArchiveDocuments an asynchronous operation that takes a delegate. Now
all the code that calls it has to be written in CPS as well. The taint
just spreads.

Getting asynchronous logic right is important, it’s only going to be
more important in the future, and the tools we have given you make you
“stand on your head and turn yourself inside out” as Professor Duggan
wisely said.

Continuation passing style is powerful, yes, but there’s got to be a
better way of making good use of that power than the code above.

Both articles, and the following series on a new C# language feature that moves all this mess into the compiler and lets you write your code as normal control flow with a special keyword to mark certain parts as asynchronous, are well worth reading even if you’re not a C# developer. I’m not, but it was still quite the enlightening experience when I ran across it for the first time.

3

The article referenced in the accepted answer is mostly true about the fact of the difficulties on reading/reasoning the CPS’d code by humans, but it is not quite convincing as the answer to this question. Moreover, it may be actually a counter argument to avoid including first-class continuations in the language designs.

CPS transformation is a general way to make the code able to express some higher-order control effects in a language without first-class continuations or other appropriate control operators, in a somewhat portable flavor. That is, if you have no chance to use them in such a language, you may choose to manually do the CPS transformation to translate the source code you have written in the direct style to some spaghetti-like one in the same language (once you really have the resources to do it correctly and manually).

On the other hand, if the language has already the ability to express such control effects, you can just write code directly using such features, without the need to transform your source code. Instead, the implementation (usually a compiler) of the language will do this (unlikely in the same target language, though) for you on demand. Note there are certain other ways to implement first-class continuations, but they usually rely on more assumptions on the non-portable details of the underlying systems (to make themselves practically implementable), so such ways are less well-known to most people.

The code with first-class continuations and higher-order control operators is indeed difficult to reason about for people without sufficient training. (Experts may suggest users to have knowledge of some formal methods to reason about the things to avoid trickery.) However, it is still not difficult as CPS’d code in general. Not only the process of CPS transformation itself, but also the result of the transformation is error-prone to read/verify to humans, even including experts. Such nature is a strong argument supporting to provide the features directly in the language to enable the implementations doing the proper works, instead of enforcing users to do unintuitive manual translations of their code.

The remained argument against first-class continuations and higher-order control operators is mainly based on the fact that such features are too low-level for most application tasks. Higher-level mechanisms like exception handling and cooperative multitasking should be more usual to average programmers (if such “high-level” features are still not too difficult to learn and to use). This is fair. However, to include a feature is not to encourage to abuse it; it is more about to avoid the embarrassment when other features are insufficient. Once the users meet the cases where the built-in high-level features are insufficient, they will sooner or later reach such low-level features, by requiring or reinventing them. Otherwise, either the need cannot be satisfied, or they have to invent a whole language implying the capability of such low-level wheels.

The conclusions:

  • They are still useful to users with needs to get the low-level works done portably, but probably not that useful to others.
  • I’m not sure the ratio of former in a typical OOP PL, but likely quite a few.
  • However, for the specific language features, the amount of users using them directly does not matter its usefulness much, because other users will usually still rely on them indirectly. Especially in the cases of low-level features, the amount of “other” users will be potentially large.
    • The only exception is that those features are too badly designed so no one ever relies on them indirectly.
    • The operator call/cc may be superseded by delimited continuations and higher-level features in practice. But the whole family of (first-class) continuations… No, I think.

I’m using this in shipping Swift Code, and since it’s not done to show my continuation skills but to solve a genuine problem, it’s quite easy to understand and would be very difficult to do without continuations.

Problem: Write a function that checks whether internet connectivity is there, and when it has decided, calls a continuation with a parameter indicating if the network is available. Obviously asynchronous because it can take a while, especially in the failure case.

Complication: This method is called from multiple threads and also repeatedly from the same thread. And having code that checks for internet access running several times is inefficient, difficult or both.

Solution: The method has an array of continuations, initially empty. First thing a call it checks whether the array is empty, otherwise there’s a call running already. If another call is running, just add the continuation to the array and return. Otherwise store the continuation as the sole array element, decide whether the network is available, and when decided extract all the continuations, set the array to empty, and call all the continuations.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>Synchronised {
If array != empty {
Add continuation to array, return
} else {
Store continuation into empty array
}
}
In the background {
Determine success/failure
Synchronised {
Make copy of array
Array = empty
}
For each continuation in copy of array {
Call continuation with success / failure
}
}
</code>
<code>Synchronised { If array != empty { Add continuation to array, return } else { Store continuation into empty array } } In the background { Determine success/failure Synchronised { Make copy of array Array = empty } For each continuation in copy of array { Call continuation with success / failure } } </code>
Synchronised {
    If array != empty {
        Add continuation to array, return
    } else {
        Store continuation into empty array
    }
}
In the background {
    Determine success/failure
    Synchronised {
        Make copy of array
        Array = empty
    }
    For each continuation in copy of array {
        Call continuation with success / failure
    }
}

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa

Are first-class continuations useful in modern object-oriented programming languages?

Continuations are extremely useful in functional programming languages (e.g. the Cont monad in Haskell) because they allow a simple and regular notation for imperative-style code. They’re also useful in some older imperative languages because they can be used to implement missing language features (e.g. exceptions, coroutines, green threads). But for a modern object-oriented language with support built in for these features, what arguments would there be for also adding support for first-class continuations (whether the more modern delimited style reset and shift or scheme-like call-with-current-continuation)?

Are there arguments against adding support other than performance and complexity of implementation?

1

Let’s let Eric Lippert answer this one:

The obvious question at this point is: if CPS is so awesome then why
don’t we use it all the time? Why have most professional developers
never heard of it, or, those who have, think of it as something only
those crazy Scheme programmers do?

First of all, it is simply hard for most people who are used to
thinking about subroutines, loops, try-catch-finally and so on to
reason about delegates being used for control flow in this way. I am
reviewing my notes on CPS from CS442 right now and I see that in 1995
I wrote down a profQUOTE: “With continuations you have to sort of
stand on your head and pull yourself inside out”. Professor Duggan (*)
was absolutely correct in saying that. Recall from a couple days ago
that our tiny little example of CPS transformation of the C#
expression M(B()?C():D()) involved four lambdas. Not everyone is good
at reading code that uses higher-order functions. It imposes a large
cognitive burden.

Moreover: one of the nice things about having specific control flow
statements baked in to a language is that they let your code clearly
express the meaning of the control flow while hiding the mechanisms –
the call stacks and return addresses and exception handler lists and
protected regions and so on. Continuations make the mechanisms of
control flow explicit in the structure of the code.
All that emphasis
on mechanism can overwhelm the meaning of the code.

In the next article, he explains how asynchrony and continuations are exactly equivalent to one another, and goes over a demonstration of taking a simple, (but blocking,) synchronous network operation and rewriting it in asyncronous style, making sure to cover all the hidden gotchas that have to be covered in order to get it right. It turns into a massive mess of code. His summary at the end:

Holy goodness, what a godawful mess we’ve made. We’ve expanded two
lines of perfectly clear code into two dozen lines of the most
godawful spaghetti you’ve ever seen. And of course it still doesn’t
even compile because the labels aren’t in scope and we have a definite
assignment error. We;d still need to further rewrite the code to fix
those problems.

Remember what I was saying about the pros and cons of CPS?

  • PRO: Arbitrarily complex and interesting control flows can be built out of simple parts – check.
  • CON: The reification of control flow via continuations is hard to read and hard to reason about – check.
  • CON: The code that represents the mechanisms of control flow completely overwhelms the meaning of the code – check.
  • CON: The transformation of ordinary code control flow into CPS is the kind of thing that compilers are good at, and almost no one else
    is – check.

This is not some intellectual exercise. Real people end up writing
code morally equivalent to the above all the time when they deal with
asynchrony.
And, ironically, even as processors have gotten faster and
cheaper, we spend more and more of our time waiting for stuff that
isn’t processor-bound.
In many programs, much of the time spent is
with the processor pegged to zero waiting for network packets to make
the trip from England to Japan and back again, or for disks to spin,
or whatever.

And I haven’t even talked about what happens if you want to compose
asynchronous operations further. Suppose you want to make
ArchiveDocuments an asynchronous operation that takes a delegate. Now
all the code that calls it has to be written in CPS as well. The taint
just spreads.

Getting asynchronous logic right is important, it’s only going to be
more important in the future, and the tools we have given you make you
“stand on your head and turn yourself inside out” as Professor Duggan
wisely said.

Continuation passing style is powerful, yes, but there’s got to be a
better way of making good use of that power than the code above.

Both articles, and the following series on a new C# language feature that moves all this mess into the compiler and lets you write your code as normal control flow with a special keyword to mark certain parts as asynchronous, are well worth reading even if you’re not a C# developer. I’m not, but it was still quite the enlightening experience when I ran across it for the first time.

3

The article referenced in the accepted answer is mostly true about the fact of the difficulties on reading/reasoning the CPS’d code by humans, but it is not quite convincing as the answer to this question. Moreover, it may be actually a counter argument to avoid including first-class continuations in the language designs.

CPS transformation is a general way to make the code able to express some higher-order control effects in a language without first-class continuations or other appropriate control operators, in a somewhat portable flavor. That is, if you have no chance to use them in such a language, you may choose to manually do the CPS transformation to translate the source code you have written in the direct style to some spaghetti-like one in the same language (once you really have the resources to do it correctly and manually).

On the other hand, if the language has already the ability to express such control effects, you can just write code directly using such features, without the need to transform your source code. Instead, the implementation (usually a compiler) of the language will do this (unlikely in the same target language, though) for you on demand. Note there are certain other ways to implement first-class continuations, but they usually rely on more assumptions on the non-portable details of the underlying systems (to make themselves practically implementable), so such ways are less well-known to most people.

The code with first-class continuations and higher-order control operators is indeed difficult to reason about for people without sufficient training. (Experts may suggest users to have knowledge of some formal methods to reason about the things to avoid trickery.) However, it is still not difficult as CPS’d code in general. Not only the process of CPS transformation itself, but also the result of the transformation is error-prone to read/verify to humans, even including experts. Such nature is a strong argument supporting to provide the features directly in the language to enable the implementations doing the proper works, instead of enforcing users to do unintuitive manual translations of their code.

The remained argument against first-class continuations and higher-order control operators is mainly based on the fact that such features are too low-level for most application tasks. Higher-level mechanisms like exception handling and cooperative multitasking should be more usual to average programmers (if such “high-level” features are still not too difficult to learn and to use). This is fair. However, to include a feature is not to encourage to abuse it; it is more about to avoid the embarrassment when other features are insufficient. Once the users meet the cases where the built-in high-level features are insufficient, they will sooner or later reach such low-level features, by requiring or reinventing them. Otherwise, either the need cannot be satisfied, or they have to invent a whole language implying the capability of such low-level wheels.

The conclusions:

  • They are still useful to users with needs to get the low-level works done portably, but probably not that useful to others.
  • I’m not sure the ratio of former in a typical OOP PL, but likely quite a few.
  • However, for the specific language features, the amount of users using them directly does not matter its usefulness much, because other users will usually still rely on them indirectly. Especially in the cases of low-level features, the amount of “other” users will be potentially large.
    • The only exception is that those features are too badly designed so no one ever relies on them indirectly.
    • The operator call/cc may be superseded by delimited continuations and higher-level features in practice. But the whole family of (first-class) continuations… No, I think.

I’m using this in shipping Swift Code, and since it’s not done to show my continuation skills but to solve a genuine problem, it’s quite easy to understand and would be very difficult to do without continuations.

Problem: Write a function that checks whether internet connectivity is there, and when it has decided, calls a continuation with a parameter indicating if the network is available. Obviously asynchronous because it can take a while, especially in the failure case.

Complication: This method is called from multiple threads and also repeatedly from the same thread. And having code that checks for internet access running several times is inefficient, difficult or both.

Solution: The method has an array of continuations, initially empty. First thing a call it checks whether the array is empty, otherwise there’s a call running already. If another call is running, just add the continuation to the array and return. Otherwise store the continuation as the sole array element, decide whether the network is available, and when decided extract all the continuations, set the array to empty, and call all the continuations.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>Synchronised {
If array != empty {
Add continuation to array, return
} else {
Store continuation into empty array
}
}
In the background {
Determine success/failure
Synchronised {
Make copy of array
Array = empty
}
For each continuation in copy of array {
Call continuation with success / failure
}
}
</code>
<code>Synchronised { If array != empty { Add continuation to array, return } else { Store continuation into empty array } } In the background { Determine success/failure Synchronised { Make copy of array Array = empty } For each continuation in copy of array { Call continuation with success / failure } } </code>
Synchronised {
    If array != empty {
        Add continuation to array, return
    } else {
        Store continuation into empty array
    }
}
In the background {
    Determine success/failure
    Synchronised {
        Make copy of array
        Array = empty
    }
    For each continuation in copy of array {
        Call continuation with success / failure
    }
}

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa

Are first-class continuations useful in modern object-oriented programming languages?

Continuations are extremely useful in functional programming languages (e.g. the Cont monad in Haskell) because they allow a simple and regular notation for imperative-style code. They’re also useful in some older imperative languages because they can be used to implement missing language features (e.g. exceptions, coroutines, green threads). But for a modern object-oriented language with support built in for these features, what arguments would there be for also adding support for first-class continuations (whether the more modern delimited style reset and shift or scheme-like call-with-current-continuation)?

Are there arguments against adding support other than performance and complexity of implementation?

1

Let’s let Eric Lippert answer this one:

The obvious question at this point is: if CPS is so awesome then why
don’t we use it all the time? Why have most professional developers
never heard of it, or, those who have, think of it as something only
those crazy Scheme programmers do?

First of all, it is simply hard for most people who are used to
thinking about subroutines, loops, try-catch-finally and so on to
reason about delegates being used for control flow in this way. I am
reviewing my notes on CPS from CS442 right now and I see that in 1995
I wrote down a profQUOTE: “With continuations you have to sort of
stand on your head and pull yourself inside out”. Professor Duggan (*)
was absolutely correct in saying that. Recall from a couple days ago
that our tiny little example of CPS transformation of the C#
expression M(B()?C():D()) involved four lambdas. Not everyone is good
at reading code that uses higher-order functions. It imposes a large
cognitive burden.

Moreover: one of the nice things about having specific control flow
statements baked in to a language is that they let your code clearly
express the meaning of the control flow while hiding the mechanisms –
the call stacks and return addresses and exception handler lists and
protected regions and so on. Continuations make the mechanisms of
control flow explicit in the structure of the code.
All that emphasis
on mechanism can overwhelm the meaning of the code.

In the next article, he explains how asynchrony and continuations are exactly equivalent to one another, and goes over a demonstration of taking a simple, (but blocking,) synchronous network operation and rewriting it in asyncronous style, making sure to cover all the hidden gotchas that have to be covered in order to get it right. It turns into a massive mess of code. His summary at the end:

Holy goodness, what a godawful mess we’ve made. We’ve expanded two
lines of perfectly clear code into two dozen lines of the most
godawful spaghetti you’ve ever seen. And of course it still doesn’t
even compile because the labels aren’t in scope and we have a definite
assignment error. We;d still need to further rewrite the code to fix
those problems.

Remember what I was saying about the pros and cons of CPS?

  • PRO: Arbitrarily complex and interesting control flows can be built out of simple parts – check.
  • CON: The reification of control flow via continuations is hard to read and hard to reason about – check.
  • CON: The code that represents the mechanisms of control flow completely overwhelms the meaning of the code – check.
  • CON: The transformation of ordinary code control flow into CPS is the kind of thing that compilers are good at, and almost no one else
    is – check.

This is not some intellectual exercise. Real people end up writing
code morally equivalent to the above all the time when they deal with
asynchrony.
And, ironically, even as processors have gotten faster and
cheaper, we spend more and more of our time waiting for stuff that
isn’t processor-bound.
In many programs, much of the time spent is
with the processor pegged to zero waiting for network packets to make
the trip from England to Japan and back again, or for disks to spin,
or whatever.

And I haven’t even talked about what happens if you want to compose
asynchronous operations further. Suppose you want to make
ArchiveDocuments an asynchronous operation that takes a delegate. Now
all the code that calls it has to be written in CPS as well. The taint
just spreads.

Getting asynchronous logic right is important, it’s only going to be
more important in the future, and the tools we have given you make you
“stand on your head and turn yourself inside out” as Professor Duggan
wisely said.

Continuation passing style is powerful, yes, but there’s got to be a
better way of making good use of that power than the code above.

Both articles, and the following series on a new C# language feature that moves all this mess into the compiler and lets you write your code as normal control flow with a special keyword to mark certain parts as asynchronous, are well worth reading even if you’re not a C# developer. I’m not, but it was still quite the enlightening experience when I ran across it for the first time.

3

The article referenced in the accepted answer is mostly true about the fact of the difficulties on reading/reasoning the CPS’d code by humans, but it is not quite convincing as the answer to this question. Moreover, it may be actually a counter argument to avoid including first-class continuations in the language designs.

CPS transformation is a general way to make the code able to express some higher-order control effects in a language without first-class continuations or other appropriate control operators, in a somewhat portable flavor. That is, if you have no chance to use them in such a language, you may choose to manually do the CPS transformation to translate the source code you have written in the direct style to some spaghetti-like one in the same language (once you really have the resources to do it correctly and manually).

On the other hand, if the language has already the ability to express such control effects, you can just write code directly using such features, without the need to transform your source code. Instead, the implementation (usually a compiler) of the language will do this (unlikely in the same target language, though) for you on demand. Note there are certain other ways to implement first-class continuations, but they usually rely on more assumptions on the non-portable details of the underlying systems (to make themselves practically implementable), so such ways are less well-known to most people.

The code with first-class continuations and higher-order control operators is indeed difficult to reason about for people without sufficient training. (Experts may suggest users to have knowledge of some formal methods to reason about the things to avoid trickery.) However, it is still not difficult as CPS’d code in general. Not only the process of CPS transformation itself, but also the result of the transformation is error-prone to read/verify to humans, even including experts. Such nature is a strong argument supporting to provide the features directly in the language to enable the implementations doing the proper works, instead of enforcing users to do unintuitive manual translations of their code.

The remained argument against first-class continuations and higher-order control operators is mainly based on the fact that such features are too low-level for most application tasks. Higher-level mechanisms like exception handling and cooperative multitasking should be more usual to average programmers (if such “high-level” features are still not too difficult to learn and to use). This is fair. However, to include a feature is not to encourage to abuse it; it is more about to avoid the embarrassment when other features are insufficient. Once the users meet the cases where the built-in high-level features are insufficient, they will sooner or later reach such low-level features, by requiring or reinventing them. Otherwise, either the need cannot be satisfied, or they have to invent a whole language implying the capability of such low-level wheels.

The conclusions:

  • They are still useful to users with needs to get the low-level works done portably, but probably not that useful to others.
  • I’m not sure the ratio of former in a typical OOP PL, but likely quite a few.
  • However, for the specific language features, the amount of users using them directly does not matter its usefulness much, because other users will usually still rely on them indirectly. Especially in the cases of low-level features, the amount of “other” users will be potentially large.
    • The only exception is that those features are too badly designed so no one ever relies on them indirectly.
    • The operator call/cc may be superseded by delimited continuations and higher-level features in practice. But the whole family of (first-class) continuations… No, I think.

I’m using this in shipping Swift Code, and since it’s not done to show my continuation skills but to solve a genuine problem, it’s quite easy to understand and would be very difficult to do without continuations.

Problem: Write a function that checks whether internet connectivity is there, and when it has decided, calls a continuation with a parameter indicating if the network is available. Obviously asynchronous because it can take a while, especially in the failure case.

Complication: This method is called from multiple threads and also repeatedly from the same thread. And having code that checks for internet access running several times is inefficient, difficult or both.

Solution: The method has an array of continuations, initially empty. First thing a call it checks whether the array is empty, otherwise there’s a call running already. If another call is running, just add the continuation to the array and return. Otherwise store the continuation as the sole array element, decide whether the network is available, and when decided extract all the continuations, set the array to empty, and call all the continuations.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>Synchronised {
If array != empty {
Add continuation to array, return
} else {
Store continuation into empty array
}
}
In the background {
Determine success/failure
Synchronised {
Make copy of array
Array = empty
}
For each continuation in copy of array {
Call continuation with success / failure
}
}
</code>
<code>Synchronised { If array != empty { Add continuation to array, return } else { Store continuation into empty array } } In the background { Determine success/failure Synchronised { Make copy of array Array = empty } For each continuation in copy of array { Call continuation with success / failure } } </code>
Synchronised {
    If array != empty {
        Add continuation to array, return
    } else {
        Store continuation into empty array
    }
}
In the background {
    Determine success/failure
    Synchronised {
        Make copy of array
        Array = empty
    }
    For each continuation in copy of array {
        Call continuation with success / failure
    }
}

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa

Are first-class continuations useful in modern object-oriented programming languages?

Continuations are extremely useful in functional programming languages (e.g. the Cont monad in Haskell) because they allow a simple and regular notation for imperative-style code. They’re also useful in some older imperative languages because they can be used to implement missing language features (e.g. exceptions, coroutines, green threads). But for a modern object-oriented language with support built in for these features, what arguments would there be for also adding support for first-class continuations (whether the more modern delimited style reset and shift or scheme-like call-with-current-continuation)?

Are there arguments against adding support other than performance and complexity of implementation?

1

Let’s let Eric Lippert answer this one:

The obvious question at this point is: if CPS is so awesome then why
don’t we use it all the time? Why have most professional developers
never heard of it, or, those who have, think of it as something only
those crazy Scheme programmers do?

First of all, it is simply hard for most people who are used to
thinking about subroutines, loops, try-catch-finally and so on to
reason about delegates being used for control flow in this way. I am
reviewing my notes on CPS from CS442 right now and I see that in 1995
I wrote down a profQUOTE: “With continuations you have to sort of
stand on your head and pull yourself inside out”. Professor Duggan (*)
was absolutely correct in saying that. Recall from a couple days ago
that our tiny little example of CPS transformation of the C#
expression M(B()?C():D()) involved four lambdas. Not everyone is good
at reading code that uses higher-order functions. It imposes a large
cognitive burden.

Moreover: one of the nice things about having specific control flow
statements baked in to a language is that they let your code clearly
express the meaning of the control flow while hiding the mechanisms –
the call stacks and return addresses and exception handler lists and
protected regions and so on. Continuations make the mechanisms of
control flow explicit in the structure of the code.
All that emphasis
on mechanism can overwhelm the meaning of the code.

In the next article, he explains how asynchrony and continuations are exactly equivalent to one another, and goes over a demonstration of taking a simple, (but blocking,) synchronous network operation and rewriting it in asyncronous style, making sure to cover all the hidden gotchas that have to be covered in order to get it right. It turns into a massive mess of code. His summary at the end:

Holy goodness, what a godawful mess we’ve made. We’ve expanded two
lines of perfectly clear code into two dozen lines of the most
godawful spaghetti you’ve ever seen. And of course it still doesn’t
even compile because the labels aren’t in scope and we have a definite
assignment error. We;d still need to further rewrite the code to fix
those problems.

Remember what I was saying about the pros and cons of CPS?

  • PRO: Arbitrarily complex and interesting control flows can be built out of simple parts – check.
  • CON: The reification of control flow via continuations is hard to read and hard to reason about – check.
  • CON: The code that represents the mechanisms of control flow completely overwhelms the meaning of the code – check.
  • CON: The transformation of ordinary code control flow into CPS is the kind of thing that compilers are good at, and almost no one else
    is – check.

This is not some intellectual exercise. Real people end up writing
code morally equivalent to the above all the time when they deal with
asynchrony.
And, ironically, even as processors have gotten faster and
cheaper, we spend more and more of our time waiting for stuff that
isn’t processor-bound.
In many programs, much of the time spent is
with the processor pegged to zero waiting for network packets to make
the trip from England to Japan and back again, or for disks to spin,
or whatever.

And I haven’t even talked about what happens if you want to compose
asynchronous operations further. Suppose you want to make
ArchiveDocuments an asynchronous operation that takes a delegate. Now
all the code that calls it has to be written in CPS as well. The taint
just spreads.

Getting asynchronous logic right is important, it’s only going to be
more important in the future, and the tools we have given you make you
“stand on your head and turn yourself inside out” as Professor Duggan
wisely said.

Continuation passing style is powerful, yes, but there’s got to be a
better way of making good use of that power than the code above.

Both articles, and the following series on a new C# language feature that moves all this mess into the compiler and lets you write your code as normal control flow with a special keyword to mark certain parts as asynchronous, are well worth reading even if you’re not a C# developer. I’m not, but it was still quite the enlightening experience when I ran across it for the first time.

3

The article referenced in the accepted answer is mostly true about the fact of the difficulties on reading/reasoning the CPS’d code by humans, but it is not quite convincing as the answer to this question. Moreover, it may be actually a counter argument to avoid including first-class continuations in the language designs.

CPS transformation is a general way to make the code able to express some higher-order control effects in a language without first-class continuations or other appropriate control operators, in a somewhat portable flavor. That is, if you have no chance to use them in such a language, you may choose to manually do the CPS transformation to translate the source code you have written in the direct style to some spaghetti-like one in the same language (once you really have the resources to do it correctly and manually).

On the other hand, if the language has already the ability to express such control effects, you can just write code directly using such features, without the need to transform your source code. Instead, the implementation (usually a compiler) of the language will do this (unlikely in the same target language, though) for you on demand. Note there are certain other ways to implement first-class continuations, but they usually rely on more assumptions on the non-portable details of the underlying systems (to make themselves practically implementable), so such ways are less well-known to most people.

The code with first-class continuations and higher-order control operators is indeed difficult to reason about for people without sufficient training. (Experts may suggest users to have knowledge of some formal methods to reason about the things to avoid trickery.) However, it is still not difficult as CPS’d code in general. Not only the process of CPS transformation itself, but also the result of the transformation is error-prone to read/verify to humans, even including experts. Such nature is a strong argument supporting to provide the features directly in the language to enable the implementations doing the proper works, instead of enforcing users to do unintuitive manual translations of their code.

The remained argument against first-class continuations and higher-order control operators is mainly based on the fact that such features are too low-level for most application tasks. Higher-level mechanisms like exception handling and cooperative multitasking should be more usual to average programmers (if such “high-level” features are still not too difficult to learn and to use). This is fair. However, to include a feature is not to encourage to abuse it; it is more about to avoid the embarrassment when other features are insufficient. Once the users meet the cases where the built-in high-level features are insufficient, they will sooner or later reach such low-level features, by requiring or reinventing them. Otherwise, either the need cannot be satisfied, or they have to invent a whole language implying the capability of such low-level wheels.

The conclusions:

  • They are still useful to users with needs to get the low-level works done portably, but probably not that useful to others.
  • I’m not sure the ratio of former in a typical OOP PL, but likely quite a few.
  • However, for the specific language features, the amount of users using them directly does not matter its usefulness much, because other users will usually still rely on them indirectly. Especially in the cases of low-level features, the amount of “other” users will be potentially large.
    • The only exception is that those features are too badly designed so no one ever relies on them indirectly.
    • The operator call/cc may be superseded by delimited continuations and higher-level features in practice. But the whole family of (first-class) continuations… No, I think.

I’m using this in shipping Swift Code, and since it’s not done to show my continuation skills but to solve a genuine problem, it’s quite easy to understand and would be very difficult to do without continuations.

Problem: Write a function that checks whether internet connectivity is there, and when it has decided, calls a continuation with a parameter indicating if the network is available. Obviously asynchronous because it can take a while, especially in the failure case.

Complication: This method is called from multiple threads and also repeatedly from the same thread. And having code that checks for internet access running several times is inefficient, difficult or both.

Solution: The method has an array of continuations, initially empty. First thing a call it checks whether the array is empty, otherwise there’s a call running already. If another call is running, just add the continuation to the array and return. Otherwise store the continuation as the sole array element, decide whether the network is available, and when decided extract all the continuations, set the array to empty, and call all the continuations.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>Synchronised {
If array != empty {
Add continuation to array, return
} else {
Store continuation into empty array
}
}
In the background {
Determine success/failure
Synchronised {
Make copy of array
Array = empty
}
For each continuation in copy of array {
Call continuation with success / failure
}
}
</code>
<code>Synchronised { If array != empty { Add continuation to array, return } else { Store continuation into empty array } } In the background { Determine success/failure Synchronised { Make copy of array Array = empty } For each continuation in copy of array { Call continuation with success / failure } } </code>
Synchronised {
    If array != empty {
        Add continuation to array, return
    } else {
        Store continuation into empty array
    }
}
In the background {
    Determine success/failure
    Synchronised {
        Make copy of array
        Array = empty
    }
    For each continuation in copy of array {
        Call continuation with success / failure
    }
}

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa

Are first-class continuations useful in modern object-oriented programming languages?

Continuations are extremely useful in functional programming languages (e.g. the Cont monad in Haskell) because they allow a simple and regular notation for imperative-style code. They’re also useful in some older imperative languages because they can be used to implement missing language features (e.g. exceptions, coroutines, green threads). But for a modern object-oriented language with support built in for these features, what arguments would there be for also adding support for first-class continuations (whether the more modern delimited style reset and shift or scheme-like call-with-current-continuation)?

Are there arguments against adding support other than performance and complexity of implementation?

1

Let’s let Eric Lippert answer this one:

The obvious question at this point is: if CPS is so awesome then why
don’t we use it all the time? Why have most professional developers
never heard of it, or, those who have, think of it as something only
those crazy Scheme programmers do?

First of all, it is simply hard for most people who are used to
thinking about subroutines, loops, try-catch-finally and so on to
reason about delegates being used for control flow in this way. I am
reviewing my notes on CPS from CS442 right now and I see that in 1995
I wrote down a profQUOTE: “With continuations you have to sort of
stand on your head and pull yourself inside out”. Professor Duggan (*)
was absolutely correct in saying that. Recall from a couple days ago
that our tiny little example of CPS transformation of the C#
expression M(B()?C():D()) involved four lambdas. Not everyone is good
at reading code that uses higher-order functions. It imposes a large
cognitive burden.

Moreover: one of the nice things about having specific control flow
statements baked in to a language is that they let your code clearly
express the meaning of the control flow while hiding the mechanisms –
the call stacks and return addresses and exception handler lists and
protected regions and so on. Continuations make the mechanisms of
control flow explicit in the structure of the code.
All that emphasis
on mechanism can overwhelm the meaning of the code.

In the next article, he explains how asynchrony and continuations are exactly equivalent to one another, and goes over a demonstration of taking a simple, (but blocking,) synchronous network operation and rewriting it in asyncronous style, making sure to cover all the hidden gotchas that have to be covered in order to get it right. It turns into a massive mess of code. His summary at the end:

Holy goodness, what a godawful mess we’ve made. We’ve expanded two
lines of perfectly clear code into two dozen lines of the most
godawful spaghetti you’ve ever seen. And of course it still doesn’t
even compile because the labels aren’t in scope and we have a definite
assignment error. We;d still need to further rewrite the code to fix
those problems.

Remember what I was saying about the pros and cons of CPS?

  • PRO: Arbitrarily complex and interesting control flows can be built out of simple parts – check.
  • CON: The reification of control flow via continuations is hard to read and hard to reason about – check.
  • CON: The code that represents the mechanisms of control flow completely overwhelms the meaning of the code – check.
  • CON: The transformation of ordinary code control flow into CPS is the kind of thing that compilers are good at, and almost no one else
    is – check.

This is not some intellectual exercise. Real people end up writing
code morally equivalent to the above all the time when they deal with
asynchrony.
And, ironically, even as processors have gotten faster and
cheaper, we spend more and more of our time waiting for stuff that
isn’t processor-bound.
In many programs, much of the time spent is
with the processor pegged to zero waiting for network packets to make
the trip from England to Japan and back again, or for disks to spin,
or whatever.

And I haven’t even talked about what happens if you want to compose
asynchronous operations further. Suppose you want to make
ArchiveDocuments an asynchronous operation that takes a delegate. Now
all the code that calls it has to be written in CPS as well. The taint
just spreads.

Getting asynchronous logic right is important, it’s only going to be
more important in the future, and the tools we have given you make you
“stand on your head and turn yourself inside out” as Professor Duggan
wisely said.

Continuation passing style is powerful, yes, but there’s got to be a
better way of making good use of that power than the code above.

Both articles, and the following series on a new C# language feature that moves all this mess into the compiler and lets you write your code as normal control flow with a special keyword to mark certain parts as asynchronous, are well worth reading even if you’re not a C# developer. I’m not, but it was still quite the enlightening experience when I ran across it for the first time.

3

The article referenced in the accepted answer is mostly true about the fact of the difficulties on reading/reasoning the CPS’d code by humans, but it is not quite convincing as the answer to this question. Moreover, it may be actually a counter argument to avoid including first-class continuations in the language designs.

CPS transformation is a general way to make the code able to express some higher-order control effects in a language without first-class continuations or other appropriate control operators, in a somewhat portable flavor. That is, if you have no chance to use them in such a language, you may choose to manually do the CPS transformation to translate the source code you have written in the direct style to some spaghetti-like one in the same language (once you really have the resources to do it correctly and manually).

On the other hand, if the language has already the ability to express such control effects, you can just write code directly using such features, without the need to transform your source code. Instead, the implementation (usually a compiler) of the language will do this (unlikely in the same target language, though) for you on demand. Note there are certain other ways to implement first-class continuations, but they usually rely on more assumptions on the non-portable details of the underlying systems (to make themselves practically implementable), so such ways are less well-known to most people.

The code with first-class continuations and higher-order control operators is indeed difficult to reason about for people without sufficient training. (Experts may suggest users to have knowledge of some formal methods to reason about the things to avoid trickery.) However, it is still not difficult as CPS’d code in general. Not only the process of CPS transformation itself, but also the result of the transformation is error-prone to read/verify to humans, even including experts. Such nature is a strong argument supporting to provide the features directly in the language to enable the implementations doing the proper works, instead of enforcing users to do unintuitive manual translations of their code.

The remained argument against first-class continuations and higher-order control operators is mainly based on the fact that such features are too low-level for most application tasks. Higher-level mechanisms like exception handling and cooperative multitasking should be more usual to average programmers (if such “high-level” features are still not too difficult to learn and to use). This is fair. However, to include a feature is not to encourage to abuse it; it is more about to avoid the embarrassment when other features are insufficient. Once the users meet the cases where the built-in high-level features are insufficient, they will sooner or later reach such low-level features, by requiring or reinventing them. Otherwise, either the need cannot be satisfied, or they have to invent a whole language implying the capability of such low-level wheels.

The conclusions:

  • They are still useful to users with needs to get the low-level works done portably, but probably not that useful to others.
  • I’m not sure the ratio of former in a typical OOP PL, but likely quite a few.
  • However, for the specific language features, the amount of users using them directly does not matter its usefulness much, because other users will usually still rely on them indirectly. Especially in the cases of low-level features, the amount of “other” users will be potentially large.
    • The only exception is that those features are too badly designed so no one ever relies on them indirectly.
    • The operator call/cc may be superseded by delimited continuations and higher-level features in practice. But the whole family of (first-class) continuations… No, I think.

I’m using this in shipping Swift Code, and since it’s not done to show my continuation skills but to solve a genuine problem, it’s quite easy to understand and would be very difficult to do without continuations.

Problem: Write a function that checks whether internet connectivity is there, and when it has decided, calls a continuation with a parameter indicating if the network is available. Obviously asynchronous because it can take a while, especially in the failure case.

Complication: This method is called from multiple threads and also repeatedly from the same thread. And having code that checks for internet access running several times is inefficient, difficult or both.

Solution: The method has an array of continuations, initially empty. First thing a call it checks whether the array is empty, otherwise there’s a call running already. If another call is running, just add the continuation to the array and return. Otherwise store the continuation as the sole array element, decide whether the network is available, and when decided extract all the continuations, set the array to empty, and call all the continuations.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>Synchronised {
If array != empty {
Add continuation to array, return
} else {
Store continuation into empty array
}
}
In the background {
Determine success/failure
Synchronised {
Make copy of array
Array = empty
}
For each continuation in copy of array {
Call continuation with success / failure
}
}
</code>
<code>Synchronised { If array != empty { Add continuation to array, return } else { Store continuation into empty array } } In the background { Determine success/failure Synchronised { Make copy of array Array = empty } For each continuation in copy of array { Call continuation with success / failure } } </code>
Synchronised {
    If array != empty {
        Add continuation to array, return
    } else {
        Store continuation into empty array
    }
}
In the background {
    Determine success/failure
    Synchronised {
        Make copy of array
        Array = empty
    }
    For each continuation in copy of array {
        Call continuation with success / failure
    }
}

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật