Fsharp: Value inside parenthesized lambda is generalized when it shouldn't be

Created on 20 Dec 2017  路  5Comments  路  Source: dotnet/fsharp

VS 2017, 15.5.2 + VSIX

Given the following code:

open System.Linq

[""].Join([""], (fun x -> x.

Expected: x is of type string, as the TOuter type in the method is a string.
Actual: x is of type 'a

If I do not surround the lambda with parentheses, x is inferred to be string:

oopsie

Area-IDE Language Service Severity-Low bug

Most helpful comment

Yes, this behavior has been there forever. I've got a habit to always close lambdas to get proper completion.

All 5 comments

I noticed this when trying to port over this test: https://github.com/Microsoft/visualfsharp/blob/master/vsintegration/tests/unittests/LegacyLanguageService/Tests.LanguageService.Completion.fs#L443

It's hard to follow what this is precisely doing (as these tests are hard to follow in general), but it seems to me that it's definitely not testing what completion looks like _as you are typing the code out_. If I copy the whole line that it's testing into the VS editor, then I'll get the expected completion list that this tests. But if I try to type it out, I get the behavior I mention above.

This makes me feel that our existing, legacy test suite for completions is suspect :(

Alternatively, it's our completion that is not smart enough, and the tests reflect this.

If I close parenthesis like in the test, string type is properly inferred:
image

Yes, this behavior has been there forever. I've got a habit to always close lambdas to get proper completion.

As an update - for generated types, there is some other behavior where if I _add_ a ( before a captured lambda value, it infers the type, but if I _don't_ have it added, it cannot infer it:

open FSharp.Data

[<Literal>]
let url = @"https://en.wikipedia.org/wiki/Megacity"

type MegaCities = HtmlProvider<url>

let printDuckFacts() =
    let data = MegaCities.GetSample()
    let tables = data.Tables

    let largestCities = tables.``Largest cities``

    let headers = largestCities.Headers

    let topFive =
        largestCities.Rows
        |> Array.take 5
        |> Array.map (fun x -> x. // <<-- x is inferred to be 'a

    printfn "Top 5 megacities: %A" topFive

screen shot 2018-05-01 at 13 32 04

If I type this instead, I get IntelliSense (note the added ():

        |> Array.map (fun x -> (x. // <<-- x is inferred to be an HTML row

screen shot 2018-05-01 at 13 32 34

cc @dsyme

This is significantly less of an issue as of automatic brace completion (which is on by default):
better

Was this page helpful?
0 / 5 - 0 ratings