Strange behavior in runtime
According to this https://github.com/dotnet/fsharp/issues/15135
type MyType<^T when ^T: (member o: int)> (t:^T) =
member inline this.T = t
Looks good in editor (no syntax error), but results
error FS1113: The value 'T' was marked inline but its implementation makes use of an internal or private function which is not sufficiently accessible
Is normal.
However
type MyType1<^T when ^T: (member o:int)> = {
t: ^T
}
with
member inline this.T = this.t
Provides very similar functionality, but working...
type MyType1<^T when ^T: (member o: int)> =
{ t: ^T }
member inline T: ^T
>
Excuse me, is anyone kind to tell what's the difference? Since according to the past investigation,
I found the underlying reason. The CLR that handles generics recognizes a number of [constraints](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/constraints-on-type-parameters), including type/interface constraints, but not explicit member constraints. The latter is a pure [F# thing](https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/generics/constraints). This means (now I am speculating) the compiler has to express it in IL by an interface constraint, and that can be done only if MyType is a "head type", i.e. statically known at compile time. These complications are also the reason why the F# Language Reference says that explicit member constraints are "not intended for common use".
Hi @Martin521,
I found an interesting stuff related to you investigation.
type a () =
member this.o x = x + 1
type A<'T> (t:'T) =
member val T = t
type MT<^T when ^T: (member o:int -> int)> (v:^T) =
inherit A<^T> (v)
module o =
let v = (MT<a> (a()) :> A<a>).T.o 1
In this case, the explicit member constraint successfully has been expressed it in IL by an interface constraint (successfully executed in fsi, SDK 9.0.103), And type ^T is NOT a "head type"... How do you think about this case?
I mean... maybe the following code snipets should be valid for F# itself (not related to CLR)
type MyType<'T when ^T: (member o: int)> (t:^T) =
member inline this.T = t
or
type MyType<'T when ^T: (member o: int)> (t:^T) =
member this.T = t
If anyone come across here, I got the workaround:
type WithBasedData<'T> (t:'T) =
member val _base = t
type MyType<^T when ^T: (member o: int)> (t:^T) =
inherit WithBasedData<'T>(t)
do
()
type MyType<^T when ^T: (member o: int)> with
member inline this.T = this._base
@ingted I missed the comment you made a month ago. I am sorry, but this is now beyond my amateur knowledge of escape analysis and related type checking procedures. But good that you found a workaround.
A much quicker workaround:
type O<'T when 'T:(member i:int)>(t:'T) as this =
[<DefaultValue>]
val mutable internalState : 'T
do
this.internalState <- t
type O<'T when 'T:(member i:int)> with
member inline this.i2 = this.internalState.i
No inherit required!!