Reputation: 2896
Given the following F# code
type MyClass = class end
type MyWorker1() =
(*1*)member this.DoWork(myObject: MyClass) = ()
(*2*)member this.DoWork(myObjects: MyClass seq) = myObjects |> Seq.iter this.DoWork //Resolves to (*1*)
type MyWorker2() =
(*3*)member this.DoWork(myObject: #MyClass) = ()
(*4*)member this.DoWork(myObjects: #MyClass seq) = myObjects |> Seq.iter this.DoWork //Resolves to (*4*)
in MyWorker2 the call this.DoWork is resolved as recursive call. I expected to behave as in MyWorker1, that is call (*3*).
Can someone explain the reason for this difference?
Upvotes: 3
Views: 92
Reputation: 57149
As already reported by @Gus on Slack, this is likely a bug, good you reported it! Note that it appears to happen only with seq
, not with other collection types like array
or list
. It also doesn't happen when you use let
bindings instead of member
or static member
.
This is probably because let
bindings cannot be self-referential unless you add rec
. By adding rec
, you actually do see the same behavior again, but this time it is expected.
I'm surprised that F# adds an extra constraint as 'a :> seq<'a>
, I find this really odd.
Since this is SO, and I'm gonna assume this blocks a certain pattern of coding, here's a workaround. It's a little extra indirection, but if you need the above pattern in real world code, here's how you can do it:
type MyWorker2() =
let doWork1(myObject: #MyClass) = ()
let doWork2(myObjects: seq<#MyClass>) = myObjects |> Seq.iter (fun x -> doWork1 x) //Resolves to (*4*)
member this.DoWork(myObject: #MyClass) = doWork1 myObject
member this.DoWork(myObjects: seq<#MyClass>) = doWork2 myObjects
One observation, if you make the private let
-bindings the same name (i.e. just doWork
instead of doWork1|2
), the issue with the extra type restriction appears on the first DoWork
. But this time it makes sense, as the let-bindings shadow one another.
Upvotes: 3