Reputation: 4295
I'm trying an experiment where I am wrapping some arrays to ensure the units on the index. I am getting a compiler error on the last line of code here. It is saying that the type parameter 'Value' cannot be used since the type parameter cannot be resolved at compile time. I cannot figure out why this is causing an error. I also tried changing the method signature to restrict the 'Value
to be the same in both arrays.
type ImArr<[<Measure>] 'Index, 'Value>(values: array<'Value>) =
// We want to make sure we have our own copy to protect against mutation
let values = values |> Array.copy
member internal _.Values = values
member this.Item
with get(index: int<'Index>) =
values.[int index]
type Arr<[<Measure>] 'Index, 'Value>(values: array<'Value>) =
// We want to make sure we have our own copy to protect against mutation
let values = values |> Array.copy
member internal _.Values = values
member inline _.Add (x: ImArr<'Index,_>) =
if values.Length <> x.Values.Length then
invalidArg (nameof x) "Cannot add arrays of different lengths"
for idx = 0 to values.Length - 1 do
values.[idx] <- values.[idx] + x.Values.[idx] // Error on this line
The other method signature I tried which also raises an error.
member inline _.Add (x: ImArr<'Index,_>) =
I also tried pulling the work out into a separate function and got the same result.
type ImArr<[<Measure>] 'Index, 'Value>(values: array<'Value>) =
// We want to make sure we have our own copy to protect against mutation
let values = values |> Array.copy
member internal _.Values= values
member this.Item
with get(index: int<'Index>) =
values.[int index]
type Arr<[<Measure>] 'Index, 'Value>(values: array<'Value>) =
// We want to make sure we have our own copy to protect against mutation
let values = values |> Array.copy
member this.Item
with get(index: int<'Index>) =
values.[int index]
and set(index: int<'Index>) (value: 'Value) =
values.[int index] <- value
member internal _.Values = values
module Helpers =
let inPlaceAdd<[<Measure>] 'Index, 'Value> (a: Arr<'Index, 'Value>) (b: ImArr<'Index, 'Value>) =
let mutable i = 0;
while i < a.Values.Length && i < b.Values.Length do
a.Values.[i] <- a.Values.[i] + b.Values.[i] // ERROR
i <- i + 1
And the compiler error
a.Values.[i] <- a.Values.[i] + b.Values.[i]
----------------------------^^^^^^^^^^^^^^^^^^^^^^^^^^^
stdin(31,29): error FS0001: The declared type parameter 'Value' cannot be used here since the type parameter cannot be resolved at compile time
Upvotes: 2
Views: 87
Reputation: 26174
This is a known issue with the F# compiler: it's better to let F# compiler infer constraints than write them by hand.
Here's a solution that allows F# compiler to infer it:
module Helpers =
let inline inPlaceAdd<[<Measure>] 'Index, .. > (a: Arr<'Index, 'Value>) (b: ImArr<'Index, 'Value>) : unit =
let mutable i = 0;
while i < a.Values.Length && i < b.Values.Length do
a.Values.[i] <- a.Values.[i] + b.Values.[i] // ERROR
i <- i + 1
Easier to write, and it doesn't produce any warning.
Upvotes: 1
Reputation: 17028
I think the main problem here is that the compiler doesn't know how to add two generic 'Value
s together. Since the compiler can't infer how to do this, you probably need to give it a hint via statically resolved type parameters (SRTP), which are, unfortunately, very flaky in F#. By trial and error, I came up with a version of your helper function that compiles with a warning but seems to work:
let inline internal inPlaceAdd<[<Measure>] 'Index, ^Value when ^Value : (static member (+) : ^Value * ^Value -> ^Value)> (a: Arr<'Index, 'Value>) (b: ImArr<'Index, 'Value>) =
let mutable i = 0;
while i < a.Values.Length && i < b.Values.Length do
let x = (^Value : (static member (+) : ^Value * ^Value -> ^Value)(a.Values.[i], b.Values.[i]))
a.Values.[i] <- x
i <- i + 1
Note the extremely convoluted invocation of addition, which causes compiler warning FS0077: Member constraints with the name 'op_Addition' are given special status by the F# compiler...
. By all rights, you should be able to add values directly instead, like this:
let x = a.Values.[i] + b.Values.[i]
But that doesn't compile, for some reason: A type parameter is missing a constraint 'when ( ^Value or ^?11121) : (static member ( + ) : ^Value * ^?11121 -> ^?11122)'
This looks like a compiler bug to me, but who knows.
Usage:
let a = Arr([|1; 2; 3|])
let b = ImArr([|2; 3; 4|])
Helpers.inPlaceAdd a b
printfn "%A" a.Values // [|3; 5; 7|]
Upvotes: 2