Can someone explain @RestrictsSuspension in plain ...
# coroutines
l
Can someone explain @RestrictsSuspension in plain english. Why is it needed, is there a performance benefit to using it?
Copy code
/**
 * Classes and interfaces marked with this annotation are restricted when used as receivers for extension
 * `suspend` functions. These `suspend` extensions can only invoke other member or extension `suspend` functions on this particular
 * receiver and are restricted from calling arbitrary suspension functions.
 */
@SinceKotlin("1.3")
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.BINARY)
public annotation class RestrictsSuspension
s
If a type (class/interface) is marked with this annotation, when I’m inside a method or extension function of this class (inside a suspended context), I can only call `suspend fun`s that are also methods or extension functions of that type.
c
It’s for creating APIs based on suspending functions which don’t use the normal async behavior of them. An example is Sequences, which is designed to be consumed synchronously but uses
suspend
to implement the
yield()
function. Sequences wouldn’t be able to be used synchronously if you could jump to other threads during their
sequence { }
block, so the suspension is restricted so that only suspend functions defined on
SequenceScope
can be called.
👍🏾 1
s
E.g this allows implementing a cooperative go-routines that are not multithreaded but allows to jump into a suspend function (suspending) and jump right back to where the caller left off (resume). Examples are
Sequences
with their
yield
function. Another example is to unwrap a contained value from its container in an imperative way, E.g. instead of
Copy code
val result: Either<Error, MyData> = repo.getResult()
result.flatMap { repo.getOtherEitherResult(it) }
you could do
Copy code
<http://Either.do|Either.do> {
    val result: MyData = repo.getResut().bind()
    repo.getOtherEitherResult(result).bind()
}
^^^ the lambda of
do
has a receiver of a type ‘scope’ that is marked with this annotation and
bind()
is defined on that same ‘scope’ (to return the value of the
flatMap
). You want to prevent the developer from calling
delay
or other `suspend fun`s not defined on that ‘scope’.
👍🏾 1
l
@streetsofboston why would it be bad to call delay? or why that reason to prevent from calling a suspend function not in the scope?
s
Because it’d need to be synchronous for these particular API examples, where
suspend
is just used for cooperative ‘single’-threading, allowing for ‘goto’ like behavior jumping into and out of other functions. If this restriction is not necessary, then you won’t need this annotation. If you want suspend-and-resume type of behavior in your interface/class’ methods and you’d like to fully control the suspend-and-resume type of behavior throughout your API (eg disallow asynchronous calls), then this annotation will help you.
l
thanks