Reputation: 57149
I've been hit by these errors before in a much more complex scenario. Since then I have simplified many things and had working code for a long time, until I needed to override Equals
. It uses an inlined member which F# doesn't seem to be happy about.
Basically, the scenario can be summarized with the following code:
[<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
The following errors are raised (this is similar to my previous question but that involved complex 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'
Now, there is no recursive value, and the target type is known (as a result of the implicit cast to SomeType
in the match pattern), so I don't think there's a reason for this inlining to be impossible.
Does anyone have some thoughts on this? Or a pattern, including the inlined op_Explicit
(if you remove getType
, you will get the error on those) and preferably also the getType
as inlined?
I know I can resolve this with an OO hierarchy, interfaces and what not, but I'd rather use this approach, both for clarity (the type system is a lattice, not a hierarchy) and for performance (an earlier version with the inlining showed more than 4x speed increase in test scenarios, and speed is important).
As an afterthought, the following, simpler scenario also throws these errors:
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
When I remove the type decoration other: SomeType
, the error disappears. I don't know why that should matter, as a narrower type with the same statically inferred methods should not raise this error, I'd think.
And since the override x.Equals
has a type annotation of obj
, I don't see how I could use that knowledge (remove the type decoration) to help me here.
Upvotes: 6
Views: 708
Reputation: 4738
It seems to be that the F# compiler can't do inlining with unordered code. As you rightly point out in the comment below this appears to be a bug.
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
Upvotes: 4