fsharp icon indicating copy to clipboard operation
fsharp copied to clipboard

Improve Error Reporting: Make FS0072 less intimidating.

Open isaacabraham opened this issue 3 years ago • 2 comments

What

The following code leads to the following error:

let bar foo =
    let age = foo.Bar + 99
    age * 2

Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved.

Why

The message is overly verbose and complicated, using words like "indeterminate" and so on. In reality, all we're trying to say is: "I couldn't infer what this type is! Please add an annotation to tell me directly.". We could also provide some guidance to explain that the compiler cannot generally infer types from C#.

How

How about:

Unable to infer the type of foo. Please add a type annotation where foo is declared e.g. (foo:string).

or

It is unclear what type foo is. Please add a type annotation where foo is declared e.g. (foo:string).

We could also add some "background" information as well:

Unable to infer the type of foo. Please add a type annotation where foo is declared e.g. (foo:string). F# can only directly infer from member usage types that are defined in F#; if the type of foo is defined in a C# library (including anything from the Base Class Library e.g. string), F# will not be able to infer it based on a member access.

isaacabraham avatar Apr 19 '22 15:04 isaacabraham

Yes. I love type inferencing but I also love the intellisense you get with member access and I want to define types that support both. Now if I can't have both, the least the compiler can do is say so plainly so that I don't become confused and frustrated by the endless pursuit of a pipe dream.

pbachmann avatar Jul 12 '22 23:07 pbachmann

@isaacabraham I agreed with you completely previously, but now I've changed my mind somewhat. The example code below seems it was misleading for you to suggest:

"F# can only directly infer from member usage types that are defined in F#; if the type of foo is defined in a C# library (including anything from the Base Class Library e.g. string), F# will not be able to infer it based on a member access."

ie. All the code below is in F# and still the type inferencing doesn’t work. Of course, your experience is much greater than mine and you may have other examples that invalidate my assessment.

I think the key to getting this error message right is prompting devs to stop wasting time pursuing the illusory goal of universal type inferencing.

How about:

“Due to limitations in F#’s type inferencing, especially when using member access, a type annotation may be needed prior to this point in the program.” ?

type Database = { Conn: SqliteConnection } with
    static member Init =
        let ret = { Conn = new SqliteConnection("Data Source=:memory:") }
        ret.Conn.Open()
        ret.Action "create table obs(ID int);"
        ret

    member this.Get sql =
        let cmd = this.Conn.CreateCommand()
        cmd.CommandText <- sql
        cmd.ExecuteScalar()

    member this.Action sql =
        let cmd = this.Conn.CreateCommand()
        cmd.CommandText <- sql
        let x = cmd.ExecuteNonQuery()
        ()

let SqlGet db sql =
    let cmd = db.Conn.CreateCommand()
    cmd.CommandText <- sql
    cmd.ExecuteScalar()

let SqlAction db sql =
    let cmd = db.Conn.CreateCommand()
    cmd.CommandText <- sql
    cmd.ExecuteNonQuery() |> ignore

let db = Database.Init

let TestDb db = 
    //db.Action "insert into obs values(1)" !!! only compiles with db parameter having type annotation (db:Database)
    //db.Get "select count(*) from obs"
    SqlAction db "insert into obs values(1)" // Works just fine
    SqlGet db "insert into obs values(1)"

printfn "result: %A"  (TestDb db)

pbachmann avatar Jul 13 '22 05:07 pbachmann