LLS
LLS

Reputation: 2228

System.Array [] not compatible with float [] [] in F#

I need to call a function that takes System.Array [] as one parameter in F#. (The function is in a library). I need to pass an argument of type float [] [] [] but the compiler refuses to compile. To replicate the problem, I wrote the following code

let x : float [] [] = Array.init 2 (fun x -> Array.zeroCreate 3)
x :> System.Array;; // This is OK

val x : float [] [] = [|[|0.0; 0.0; 0.0|]; [|0.0; 0.0; 0.0|]|]

> x :> System.Array [];; //Error

  x :> System.Array [];;
  ^^^^^^^^^^^^^^^^^^^^

stdin(14,1): warning FS0059: The type 'System.Array []' does not have any proper subtypes and need not be used as the target of a static coercion

  x :> System.Array [];;
  ^^^^^^^^^^^^^^^^^^^^

stdin(14,1): error FS0193: Type constraint mismatch. The type 
    float [] []    
is not compatible with type
    System.Array []    
The type 'System.Array' does not match the type 'float []'

How can I solve this problem?

Thanks in advance.

Upvotes: 1

Views: 731

Answers (2)

kvb
kvb

Reputation: 55185

The ability to treat an 's[] as a 't[] when 's :> 't makes the .NET type system unsound (and is probably due to the fact that Java does the same thing). Unfortunately, C# follows .NET in allowing this.

Because it's a .NET runtime feature, you can also do it in F# via boxing and unboxing:

let x : float[][] = Array.init 2 (fun x -> Array.zeroCreate 3)
let x' = (box x) :?> System.Array[]

This avoids the overhead of mapping each element as in Ramon's solution.

To see why this makes the .NET type system unsound, consider this:

x'.[0] <- upcast [| "test" |] // System.ArrayTypeMismatchException

Even though we are storing a value of type System.Array into a System.Array[], we get an exception at runtime because the true type of the underlying array can't support the operation (x and x' are just two views of the same array, and obviously a string[] can't be stored into x). This unsoundness in the .NET type system therefore has the undesirable side effect of requiring additional overhead for most stores into arrays to ensure that the stored value has a type compatible with the underlying array. In my opinion, it's a good thing that F# prevents you from doing this directly.

Upvotes: 4

Ramon Snir
Ramon Snir

Reputation: 7560

You could do this:

let x : float [] [] = Array.init 2 (fun x -> Array.zeroCreate 3)

let toArray (xs : #System.Array []) =
    Array.map (fun x -> x :> System.Array) xs

let x' : System.Array [] = toArray x

Upvotes: 2

Related Questions