Skip to content

Confusing type inference error in task expression #13789

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
jwosty opened this issue Aug 26, 2022 · 2 comments · Fixed by #18450
Closed

Confusing type inference error in task expression #13789

jwosty opened this issue Aug 26, 2022 · 2 comments · Fixed by #18450
Labels
Area-Diagnostics mistakes and possible improvements to diagnostics Feature Improvement
Milestone

Comments

@jwosty
Copy link
Contributor

jwosty commented Aug 26, 2022

Not sure whether or not this is a bug, or should be a feature request, but I ran into this and found it incredibly confusing.

It seems that the task CE produces certain kinds of typing errors on the wrong lines, where the equivalent async expression produces no errors at all.

Repro Steps

Compile the following code:

open System.Threading.Tasks
open System.Collections.Generic
open System.Linq
let f () : Task<IList<string>> = task {
    let! x = task { return 42 }
    let! y = task { return 43 }
    return Seq.empty.ToList()
}

Expected behavior

This code should either compile with no problems, or at least produce a compiler error on the return line (as I understand that usually F# won't automatically upcast Task<List<string>> to Task<IList<string>>).

Actual behavior

A type error is emitted on the first let! line, which is NOT the actual source of the typing error:

      let! x = task { return 42 }
  ----^^^^^^^^^^^^^^^^^^^^^^^^^^^

/stdin(5,5): error FS0193: Type constraint mismatch. The type 
    'TaskCode<List<'a>,List<'a>>'    
is not compatible with type
    'TaskCode<IList<string>,IList<string>>'

This is particularly confusing when there's a lot of code between the first let! and the final return and/or if the let! is a very complex expression, as it leads you to look in the entirely wrong place for the problem.

Known workarounds

Add an extra annotation on the return line, like so:

open System.Threading.Tasks
open System.Collections.Generic
open System.Linq
let f () : Task<IList<string>> = task {
    let! x = task { return 42 }
    let! y = task { return 43 }
    return (Seq.empty.ToList() : IList<string>)
}

Related Information

It should be noted that this does not occur in equivalent async expressions:

open System.Threading.Tasks
open System.Collections.Generic
open System.Linq
let g () : Async<IList<string>> = async {
    let! x = async { return 42 }
    let! y = async { return 43 }
    return Seq.empty.ToList()
}
@jwosty jwosty added the Bug label Aug 26, 2022
@jwosty
Copy link
Contributor Author

jwosty commented Aug 26, 2022

Potentially related to #12929

@vzarytovskii vzarytovskii added the Area-Compiler-StateMachines Sequence, list, task and other state machine compilation label Aug 26, 2022
@vzarytovskii vzarytovskii added this to the Backlog milestone Sep 9, 2022
@vzarytovskii vzarytovskii moved this to Not Planned in F# Compiler and Tooling Sep 9, 2022
@dsyme dsyme added Feature Improvement Area-Diagnostics mistakes and possible improvements to diagnostics and removed Bug Area-Compiler-StateMachines Sequence, list, task and other state machine compilation labels Sep 23, 2022
@TheAngryByrd
Copy link
Contributor

TheAngryByrd commented Jun 14, 2023

I ended up hitting this yesterday while converting older task CE's to the F# built in one.

Another example:

        // Works
        let foo () : int64 = 6
        let otherAsync () = async { return "lol"}
        let fooAsync () : Async<int64> = async { 
            let! _ = otherAsync ()
            return 6 
        }
        // Doesn't work
        //Type constraint mismatch. The type 
        //     'TaskCode<int,int>'    
        // is not compatible with type
        //     'TaskCode<int64,int64>'  
        let otherTask = task { return "lol"}
        let fooTask () : Task<int64> = task { 
                let! _ = otherTask ()
                return 6 
            }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Diagnostics mistakes and possible improvements to diagnostics Feature Improvement
Projects
Archived in project
Development

Successfully merging a pull request may close this issue.

4 participants