ca9163d9
ca9163d9

Reputation: 29159

The mutable variable 'index' is used in an invalid way in seq {}?

In the following code, the compiler gets error on index <- index + 1 with error

Error 3 The mutable variable 'index' is used in an invalid way. Mutable variables cannot be captured by closures. Consider eliminating this use of mutation or using a heap-allocated mutable reference cell via 'ref' and '!'. d:\Users....\Program.fs 11 22 ConsoleApplication2

However, it has been defined as mutable?

let rec iterateTupleMemberTypes (tupleArgTypes: System.Type[]) (columnNames: string[]) startingIndex = 
    seq {
        let mutable index = startingIndex
        for t in tupleArgTypes do
            match t.IsGenericType with
            | true -> iterateTupleMemberTypes (t.GetGenericArguments()) columnNames index |> ignore
            | false ->
                printfn "Name: %s Type: %A" (columnNames.[index]) t
                index <- index + 1
                yield (columnNames.[index]), t
    } |> Map.ofSeq

let myFile = CsvProvider<"""d:\temp\sample.txt""">.GetSample()
let firstRow = myFile.Rows |> Seq.head
let tupleType = firstRow.GetType()
let tupleArgTypes = tupleType.GetGenericArguments()
let m = iterateTupleMemberTypes tupleArgTypes myFile.Headers.Value 0

Upvotes: 0

Views: 122

Answers (3)

CaringDev
CaringDev

Reputation: 8551

An idiomatic version of this might look like the following:

#r @"..\packages\FSharp.Data.2.2.2\lib\net40\FSharp.Data.dll"

open FSharp.Data
open System

type SampleCsv = CsvProvider<"Sample.csv">

let sample = SampleCsv.GetSample()

let rec collectLeaves (typeTree : Type) =
    seq {
        match typeTree.IsGenericType with
        | false -> yield typeTree.Name
        | true -> yield! typeTree.GetGenericArguments() |> Seq.collect collectLeaves
    }

let columnTypes = (sample.Rows |> Seq.head).GetType() |> collectLeaves

let columnDefinitions = columnTypes |> Seq.zip sample.Headers.Value |> Map.ofSeq

let getDefinitions (sample : SampleCsv) = (sample.Rows |> Seq.head).GetType() |> collectLeaves |> Seq.zip sample.Headers.Value |> Map.ofSeq

Personally, I wouldn't be concerned too much about the performance of Map vs Dictionary (and rather have the immutable Map) unless there are hundreds of columns.

Upvotes: 2

ca9163d9
ca9163d9

Reputation: 29159

Suggested by @Ming-Tang, I changed the mutable variable to ref and it works now. However, is it a way not to use mutable/ref variable at all?

let rec iterateTupleMemberTypes (tupleArgTypes: System.Type[]) (columnNames: string[]) startingIndex = 
    seq {
        let index = ref startingIndex
        for t in tupleArgTypes do
            match t.IsGenericType with
            | true ->
                yield! iterateTupleMemberTypes (t.GetGenericArguments()) columnNames !index
            | false ->
                printfn "Name: %s Type: %A" (columnNames.[!index]) t
                yield (columnNames.[!index]), t
                index := !index + 1
    } |> dict

let myFile = CsvProvider<"""d:\temp\sample.txt""">.GetSample()
let firstRow = myFile.Rows |> Seq.head
let tupleType = firstRow.GetType()
let tupleArgTypes = tupleType.GetGenericArguments()
let m = iterateTupleMemberTypes tupleArgTypes myFile.Headers.Value 0

Upvotes: 0

Ming-Tang
Ming-Tang

Reputation: 17651

The statement after it, let index = 0, shadows your definition of mutable variable index. Also, to make mutables work in sequences, you need refs. https://msdn.microsoft.com/en-us/library/dd233186.aspx

Upvotes: 1

Related Questions