WiseIndian / Coroutines-for-Scala-3

3 stars 2 forks source link

Support function definitions within the coroutine body #10

Open WiseIndian opened 4 years ago

WiseIndian commented 4 years ago

Those functions definitions should support yieldval in their body. We need to formalize this solution.

WiseIndian commented 4 years ago

If a function is defined outside of the coroutine body or if it doesnt contain yieldval within the body of its definition it is left as is. Note that a function must also not contain calls to functions that contain any yieldval within them in order to not be transformed.

If the function body contains yieldval or calls functions whose body contains yieldval then it is transformed to a coroutine. We will transform the function definitions to a coroutine that returns either the type that is returned by the surrounding coroutine body or that returns an expression of the type the return type of the function.

We do a first transformation phase: We visit every expression e of the function body to change types to the Either type where needed. The following partial function would be used as input to a function visiting every node of the tree of the function body.

e match {
    case '{yieldval(e)} => yieldval(Left(e))
    case '{return e} => yieldval(Right(e))
}

we would also transform the last expression e of the function body to yieldval(Right(e)).

Then we would need to do another transformation of the function definition:

[[ def foo(e1, ..., en): R = { Z* }  ]](c) = 
'{
    def foo(e1, ..., en) = new Coroutine[Either[T, R]] {
        def continue: Option[Either[T, R]] = [[Z*]](c)
    }

    ${c()}
}

Class method calls are left as they are for the moment. A function call to a function definition containing yieldval could then be in principle transformed to something like this:

[[foo(e1, ..., en)]](c) = 
'{
    val fooCoroutine: Coroutine[Either[T,R]] = foo()
    [[
        val returnValue: R = null
        while (!fooCoroutine.isDone()) {
            val result: Either[T, R] = fooCoroutine.continue
            if (result isLeft) {
                yieldval(result.left.get)
            } else {
                returnValue = result.right.get
            }
        }
        returnValue
    ]](c)
}

This would allow recursive function call and mutual function calls.

liufengyun commented 4 years ago

@LeDevDuDimanche Do you have use cases in mind that depend on this? Otherwise, I'd say to delay this. Just use type checking to report an error if user writes code like this. WDYT?

WiseIndian commented 4 years ago

I didnt really have any case in mind. I guess we can delay this.