Somehow compiler cannot get member of successfully inferred type:

Provide the steps required to reproduce the problem
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.fs" />
<PackageReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
</Project>
Program.fsmodule WebApplication2
open Microsoft.AspNetCore
open Microsoft.AspNetCore.Hosting
open Microsoft.Extensions.DependencyInjection
type Foo() =
member val Bar = "" with get, set
type Startup private () =
member __.ConfigureServices(services: IServiceCollection) =
services.Configure<Foo>(fun foo -> printfn "%s" foo.Bar)
|> ignore
[<EntryPoint>]
let main args =
WebHost
.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.Build()
.Run()
0
dotnet buildThese two files in attach:
WebApplication2.zip
It should compile
Compiler actually inferred foo type in lambda correctly, but fails to access type properties
Provide any related information
Inference with class members never works (though arguably it could work perhaps on sealed classes, but to the best of my knowledge that has not been implemented). If you change your poco to a record or DU, type inference will work correctly.
@abelbraaksma If I just create C# library with this method

and call it like this (with same class definition)

It works OK. So there is something else beside being Foo a class
I spoke too soon, you're right. Though I'm unsure whether both examples are fully interchangeable, and it's a common nuisance I have that with class members type inference often fails. But with the syntax you use, it ought to work correctly, I agree.
Definitely feels like a bug in the compiler if it works for a generic action you define yourself.
A workaround is this:
member this.ConfigureServices(services: IServiceCollection) =
services.Configure<Foo>(configureOptions = fun foo -> printfn "%s" foo.Bar)
|> ignore
Annotating the parameter with configureOptions seems to resolve it.
I believe this might have something to do with overloading, because I can reproduce it when I add an overload method:
type Foo() =
member val Bar = "" with get, set
type TestClass() =
member __.Test<'T when 'T : not struct>(f: System.Action<'T>) = ()
member __.Test<'T when 'T : not struct>(name: string) = ()
member this.Do() =
this.Test<Foo>(fun foo -> printfn "%s" foo.Bar) // same error
this.Test<Foo>(f = fun foo -> printfn "%s" foo.Bar) // works
Removing the overload, everything works.
type Foo() =
member val Bar = "" with get, set
type TestClass() =
member __.Test<'T when 'T : not struct>(f: System.Action<'T>) = ()
member this.Do() =
this.Test<Foo>(fun foo -> printfn "%s" foo.Bar) // works
this.Test<Foo>(f = fun foo -> printfn "%s" foo.Bar) // works
Feels like a bug to me. If it's by design then we should probably improve the behavior.
It's by design. For overloaded methods, the type is propagated into lambdas if all method overloads accept a lambda. However in this case the overloads take string and Action, and the type is not pre-inferred. Just add the type annotation.