在 FS1114、FS1113、FS1116、FS1118 中使用内联类型成员失败

Using inline within type members fails with FS1114, FS1113, FS1116, FS1118

我一直被这些错误击中 。从那时起,我简化了很多事情,并且有很长时间的工作代码,直到我需要重写 Equals。它使用 F# 似乎不喜欢的内联成员。

基本上,场景可以用以下代码概括:

[<Flags>]
type MyType =
    | Integer = 0b0001
    | Float   = 0b0010

module Test =
    [<CustomEquality;NoComparison>]
    type SomeType =
        | Int of int64
        | Float of float

        override x.Equals other =
            match other with
            | :? SomeType as y -> 
                // following line throws on compiling this
                match SomeType.getType x &&& SomeType.getType y with
                | MyType.Integer -> int64 x = int64 y       // highest type is integer (both are int)
                | MyType.Float -> float x = float y         // highest type is float (either is or both are float)
                | _ -> false // impossible
            | _ -> false

        override x.GetHashCode() =
            match x with Int i -> hash i | Float f -> hash f

        static member inline op_Explicit(n: SomeType): float =
            match n with
            | Int i -> float i
            | Float f -> f

        static member inline op_Explicit(n: SomeType): int64 =
            match n with
            | Int i ->  i
            | Float f -> int64 f

        static member inline getType x =
            match x with
            | Int _ -> MyType.Integer
            | Float _ -> MyType.Float

出现了以下错误(这与我之前的问题类似,但涉及复杂的 ducktyping)。

error FS1114: The value 'Test.SomeType.getType' was marked inline but was not bound in the optimization environment
error FS1113: The value 'getType' was marked inline but its implementation makes use of an internal or private function which is not sufficiently accessible
warning FS1116: A value marked as 'inline' has an unexpected value
error FS1118: Failed to inline the value 'getType' marked 'inline', perhaps because a recursive value was marked 'inline'

现在,没有递归值,并且目标类型是已知的(作为匹配模式中隐式转换为 SomeType 的结果),所以我认为没有理由这样做内联是不可能的。

有人对此有什么想法吗?或者一个模式,包括内联的 op_Explicit(如果你删除 getType,你会得到那些错误)并且最好还有内联的 getType?

我知道我可以用 OO 层次结构、接口等等来解决这个问题,但我宁愿使用这种方法,既是为了清晰(类型系统是一个格子,而不是层次结构)也是为了性能(一个带有内联的早期版本在测试场景中显示速度提高了 4 倍以上,速度很重要)。

事后,以下更简单的场景也会引发这些错误:

module Test =
    type SomeType =
        | Int of int64
        | Float of float

        static member MyEquals (x, other: SomeType) =
            // following line throws on compiling this
            float x = float other 

        static member inline op_Explicit(n: SomeType): float =
            match n with
            | Int i -> float i
            | Float f -> f

        static member inline op_Explicit(n: SomeType): int64 =
            match n with
            | Int i ->  i
            | Float f -> int64 f

当我删除类型修饰 other: SomeType 时,错误消失了。我不知道为什么这很重要,因为我认为具有相同静态推断方法的较窄类型不应引发此错误。

并且由于 override x.Equals 具有 obj 的类型注释,我不知道如何使用该知识(删除类型装饰)来帮助我。

似乎是 F# 编译器无法对无序代码进行内联。正如您在下面的评论中正确指出的那样,这似乎是一个错误。

open System

[<Flags>]
type MyType =
    | Integer = 0b0001
    | Float   = 0b0010

module Test =
    [<CustomEquality;NoComparison>]
    type SomeType =
        | Int of int64
        | Float of float

        static member inline op_Explicit(n: SomeType): float =
            match n with
            | Int i -> float i
            | Float f -> f

        static member inline op_Explicit(n: SomeType): int64 =
            match n with
            | Int i ->  i
            | Float f -> int64 f

        static member inline getType x =
            match x with
            | Int _ -> MyType.Integer
            | Float _ -> MyType.Float  

        override x.Equals other =
            match other with
            | :? SomeType as y -> 
                // following line throws on compiling this
                match SomeType.getType x &&& SomeType.getType y with
                | MyType.Integer -> int64 x = int64 y       // highest type is integer (both are int)
                | MyType.Float -> float x = float y         // highest type is float (either is or both are float)
                | _ -> false // impossible
            | _ -> false

        override x.GetHashCode() =
            match x with Int i -> hash i | Float f -> hash f