1

I am looking to convert this code to use F# list instead of the C# list implementation.

I am connecting to a database and running a query usually with C# would create a list of a type and keep adding the list while the datareader has values. How would I go about converting this to use an F# list

    let queryDatabase (connection: NpgsqlConnection) (queryString: string) =
        let transactions = new List<string>()
        let command = new NpgsqlCommand(queryString, connection)
        let dataReader = command.ExecuteReader()

        while dataReader.Read() do
           let json = dataReader.GetString(1)
           transactions.Add(json)
        transactions
Alan Mulligan
  • 1,107
  • 2
  • 16
  • 35
  • 1
    See [:: (cons)](https://msdn.microsoft.com/en-us/library/dd233224.aspx) operator. Also note that C# List<> is a wrapper around an array, whereas F# list is a pure linked-list. F# list is very fast to add elements, but uses more memory – Ivan Nechipayko Feb 26 '15 at 11:25

2 Answers2

6

The tricky thing here is that the input data source is inherently imperative (you have to call Read which mutates the internal state). So, you're crossing from imperative to a functional world - and so you cannot avoid all mutation.

I would probably write the code using a list comprehension, which keeps a similar familiar structure, but removes explicit mutation:

let queryDatabase (connection: NpgsqlConnection) (queryString: string) =
  [ let command = new NpgsqlCommand(queryString, connection)
    let dataReader = command.ExecuteReader()
    while dataReader.Read() do
      yield dataReader.GetString(1) ]
Tomas Petricek
  • 240,744
  • 19
  • 378
  • 553
1

Tomas' answer is a solution to use in product code. But for sake of learning F# and functional programming I present my snippet with tail recursion and cons operator:

let drToList (dr:DataReader) =
    let rec toList acc =
        if not dr.Read then acc
        else toList <| dr.GetString(1) :: acc
    toList []

This tail recursion function is compiled into imperative-like code, thus no stack overflow and fast execution.

Also I advice you look at this C# thread and this F# documentation to see how properly dispose your command. Basically, you need to use smth like this:

let queryDb (conn: NpgsqlConnection) (qStr: string) =
   use cmd = new NpgsqlCommand(qStr, conn)
   cmd.ExecuteReader() |> drToList

And if we go deeper, you should also think about exception handling.

Community
  • 1
  • 1