Charles Lambert
Charles Lambert

Reputation: 5132

this expression was expected to have type IDataReader but here has type SqlDataReader

The following code is not casting my return value of SqlDataReader from getReader correctly to IDataReaderin the call to Seq.unfold. What am I doing wrong?

open System.Data
open System.Data.SqlClient
open System.Configuration

type Foo = { id:int; name:string }

let populateFoo (r:IDataReader) =
    let o = r.GetOrdinal
    { id = o "id" |> r.GetInt32; name = o "name" |> r.GetString; }

let iter populateObject (r:IDataReader)  =
    match r.Read() with
    | true -> Some(populateObject r, r)
    | _    -> None

let iterFoo = iter populateFoo

let getReader : IDataReader =
    let cnstr = ConfigurationManager.ConnectionStrings.["db"].ConnectionString
    let cn = new SqlConnection(cnstr)
    let cmd = new SqlCommand("select * from Foo", cn)
    cmd.ExecuteReader()

let foos = Seq.unfold iterFoo getReader

Upvotes: 1

Views: 137

Answers (1)

Gus
Gus

Reputation: 26174

F# does not automatic upcasting like C#, except in some specific scenarios (see the spec, section 14.4.2).

You have to explicitly cast the expression: cmd.ExecuteReader() :> IDataReader then you can remove the type annotation after getReader.

Alternatively you may leave that function returning an SqlDataReader and upcast at the call site:

let foos = getReader :> IDataReader |> Seq.unfold iterFoo

If unfold was a static member of a type with a signature like this one:

type T() =
    static member unfold(a, b:IDataReader) = Seq.unfold a b

you would be able to do directly T.unfold(iterFoo, getReader) and it will automatically upcast. That's one of the cases mentioned in the spec.

Upvotes: 3

Related Questions