Destino
Destino

Reputation: 475

How to receive a type that extends an interface without losing the original type

I have just started using F# and my brain is broken trying to figure out how to work with its types without having to resort to an OO type of programming.

Here is my situation I basically want to create a method where I provide the type and the Id and it returns to me the object on the database.

So basically this is what I get so far.

let client = MongoClient()
let database = client.GetDatabase("testdb")

let lowerCase (str : string) = 
    str.ToLower()

let nameOf (classType: Type) = 
    classType.Name

let nameTypeOf<'a> = 
    nameOf typeof<'a>   

let getCollection<'a> =
    let collectionName = nameTypeOf<'a> |> lowerCase
    database.GetCollection<'a> collectionName

let dbSelect<'a> id = 
    let collection = getCollection<'a>
    collection.Find(fun(x) -> x.Id = id).First()

So my problem is with the dbSelect, obviously it does not compile since x is generic, basically I wanted to create an interface with the Id and all my objects interface with it. I do know how to do it using classes and inheritances, but I am avoiding having to use instanced classes outside interop with c# libraries. What would be the best functional way to do it, if there is any.

This is what I was eexpecting to call it with

type IDbObject = 
    abstract Id: string

type Item = 
    {
        Id: string
        Name: string
    }
    interface IDbObject with
        member x.Id = x.Id

let item = 
    selectDb<Item> "5993592a35ce962b80da1e22"

Any help would be appreciated. And if anyone want to point out how crappy my code is, any feedback is really appreciated

Upvotes: 3

Views: 118

Answers (1)

scrwtp
scrwtp

Reputation: 13577

I don't think the solution here is much different from what you'd have in C#. You can constrain the generic type to use the interface members, getting something roughly like this:

let getCollection<'a when 'a :> IDbObject> () =
   let collectionName = nameTypeOf<'a> |> lowerCase
   database.GetCollection<'a> collectionName

let dbSelect<'a when 'a :> IDbObject> id =
   let collection = getCollection<'a>()
   collection.Find(fun (x : 'a) -> x.Id = id).First()

The type of dbSelect should be inferred to be string -> #IDbObject, and be coerced to string -> 'a at the call site.

Upvotes: 5

Related Questions