Skip to content

Computation expressions should support syntax desugaring for return!/yield! in tailcall positions #1006

@dsyme

Description

@dsyme

Computation expressions like task, seq, taskSeq, async, coroutine often need to support tailcalls in the computational structure, e.g.

let rec f n = 
    seq { 
        ...do something... ; 
        if n > 0 then
            yield! f (n-1) 
    }

Here tailcalling means that, once the first sequence is done its work, it delegates to the next sequence, and that chains of sequences either "cut out" or otherwise relinquish resources.

Tailcall detection of yield! is built in to the F# compiler for its compilation of sequence expressions (see this). However for other computation expressions this is harder. The problem is that return! and yield! are often used in non-tailcall positions, e.g. inside a try/with or try/finally, or sequencing of yield!. This makes it awkward to implement tailcalling semantics, especially for computation expressions that can be compiled with resumable code. Workarounds do exist but they increase the amount of code generated and have other problems.

Instead, we should simply augment computation expression de-sugaring to desugar return!/yield! to ReturnFromFinal/YieldFromFinal (or some other name) when they occur at the natural tailcall position if the method is present on the computation expression builder. Likewise do! in the final position can translate to ReturnFromFinal if present.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions