John Palmer
John Palmer

Reputation: 25516

For loops in 2D arrays giving incorrect types in F#

I suspect that I am missing something very obvious here but this doesn't work:

let t = Array2D.create 1 1 1.0
for x in t do printfn "%f" x;;

It fails with

error FS0001: The type 'obj' is not compatible with any of the types float,float32,decimal, arising from the use of a printf-style format string

Interestingly using printf "%A" or "%O" prints the expected values which suggests to me that the problem is with the type inference

The corresponding code for a 1D array works fine

let t = Array.create 1 1.0
for x in t do printfn "%f" x;;

For reference this is on version 2.0 (both interactive and compiler) running on the latest mono

Upvotes: 0

Views: 655

Answers (2)

Ramon Snir
Ramon Snir

Reputation: 7560

As Jack pointed out, this is a problem. One easy solution is:

let t = Array2D.create 2 2 1.0
t |> Array2D.iter (printfn "%f");;

And if you really like the for .. in .. do syntax:

type Array2DForLoopBuilder() =
    member __.Zero() = ()
    member __.For(a, f) = Array2D.iter f a
    member __.Run e = e

let a2dfor = Array2DForLoopBuilder()

let t = Array2D.init 2 2 (fun a b -> float a + float b)

a2dfor { for x in t do printfn "%f" x }

Upvotes: 3

Jack P.
Jack P.

Reputation: 11525

In .NET, a 1D array implicitly implements IList, which means it also implements (by inheritance) IEnumerable<T>. So, when you run:

let t = Array.create 1 1.0
for x in t do printfn "%f" x;;

the F# compiler emits code which gets an implementation of IEnumerable<T> (seq<T> in F#) from t, then iterates over it. Since it's able to get an IEnumerable<T> from the array, x will have type T.

On the other hand, multi-dimensional arrays (2d, 3d, etc.) only implement IEnumerable (not IEnumerable<T>) so the F# compiler infers the type of x as System.Object (or obj, in F#).

There are two solutions for what you want:

Cast each individual value within the loop, before printing it:

for x in t do printfn "%f" (x :?> float);;

Or, use Seq.cast to create and iterate over a strongly-typed enumerator:

for x in (Seq.cast<float> t) do printfn "%f" x;;

Upvotes: 7

Related Questions