Skip to content

F# fails to determine which overload of TaskSeqBuilder.Using to use. #97

@peterfaria-lula

Description

@peterfaria-lula

F# fails to determine which overload of TaskSeqBuilder.Using to use when type implements both IDisposable and IAsyncDisposable.

A real-world example with the NpgsqlDataReader type from Npgsql:

#r "nuget: Npgsql"
#r "nuget: FSharp.Control.TaskSeq"

open Npgsql
open FSharp.Control


let readRows (command: NpgsqlCommand) (ct: CancellationToken) f = taskSeq {
    use! reader = command.ExecuteReaderAsync ct // Fails to compile.
    
    let! reader = command.ExecuteReaderAsync ct
    use reader = reader // Also fails to compile.
}

Below is a set of test cases replicating the issue.

#r "nuget: FSharp.Control.TaskSeq"
#r "nuget: Xunit"
#r "nuget: FsUnit"

open System
open System.Threading.Tasks
open FSharp.Control
open FsUnit
open Xunit


type private OneGetter() =
    member _.Get1() = 1

type private Disposable() =
    inherit OneGetter()

    interface IDisposable with
        member _.Dispose() = ()

type private AsyncDisposable() =
    inherit OneGetter()

    interface IAsyncDisposable with
        member _.DisposeAsync() = ValueTask()

type private MultiDispose() =
    inherit OneGetter()

    interface IDisposable with
        member _.Dispose() =
            ()

    interface IAsyncDisposable with
        member _.DisposeAsync() =
            ValueTask()

let private check ts = task {
    let! length = ts |> TaskSeq.length
    length |> should equal 1
}

[<Fact>]
let ``CE task: Using when type implements IDisposable``() =
    let ts = taskSeq {
        use x = new Disposable()

        yield x.Get1()
    }

    check ts

[<Fact>]
let ``CE task: Using when type implements IAsyncDisposable``() =
    let ts = taskSeq {
        use x = AsyncDisposable()
        yield x.Get1()
    }

    check ts


[<Fact>]
let ``CE task: Using when type implements IDisposable and IAsyncDisposable``() =
    let ts = taskSeq {
        use x = new MultiDispose() // Fails to compile
        yield x.Get1()
    }

    check ts

[<Fact>]
let ``CE task: Using! when type implements IDisposable``() =
    let ts = taskSeq {
        use! x = task { return new Disposable() }
        yield x.Get1()
    }

    check ts


[<Fact>]
let ``CE task: Using! when type implements IAsyncDisposable``() =
    let ts = taskSeq {
        use! x = task { return AsyncDisposable() }
        yield x.Get1()
    }

    check ts


[<Fact>]
let ``CE task: Using! when type implements IDisposable and IAsyncDisposable``() =
    let ts = taskSeq {
        use! x = task { return new MultiDispose() } // Fails to compile
        yield x.Get1()
    }

    check ts

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtopic: taskseq-ceRelated to the taskseq computation expression builders or overloads

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions