1

I'm trying to create an abstraction for a lightweight data storage module using an F# signature file. Here is my signature file code, let's say it's called repository.fsi

namespace DataStorage

/// <summary>Lightweight Repository Abstraction</summary>
module Repository = 
   /// <summary> Insert data into the repository </summary>
   val put: 'a -> unit
   /// <summary> Fetch data from the repository </summary>
   val fetch: 'a -> 'b
   /// <summary> Remove data from the repository </summary>
   val remove: 'a -> unit

Here is the corresponding implementation, let's call it repository.fs

namespace DataStorage

module Repository = 

    (* Put a document into a database collection *)
    let put entity = ()

    (* Select a document from a database collection *)
    let fetch key = ("key",5)

    (* Remove a document from a database collection *)
    let remove entity = ()

In my visual studio project file I have the signature file (repository.fsi) above my implementation file (repository.fs). The put and remove functions are being parsed and verified correctly with no errors (in the implementation file) but the fetch function keeps giving me the red squiggly in visual studio with the following error message:

Module 'DataStorage.Repository' contains

val fetch: s:string -> string * int

but its signature specifies

val fetch<'a,'b> : 'a -> 'b

The respective type parameter counts differ

Can someone tell me what I'm doing wrong? Is my fetch function value defined wrong in my signature file? I'm just trying to create a generic function ('a -> 'b) in my signature file and have the implementation take one type as input and return a different type as output.

cameron
  • 81
  • 6
  • 1
    The signature for `fetch` is generic but the implementation isn't. If the implementation is just a stub, try replacing it with `let fetch key = Unchecked.defaultof<_>` to allow it to compile. – Daniel Apr 18 '13 at 14:21

2 Answers2

2

I am not very strong in F# yet but I think here you are using signature files in wrong way.

First. Here is how you can fix compilation errors:

Replace:

let fetch key = ("key",5)

with:

let fetch key = ("key",5) :> obj :?> 'b

and you won't get compilation errors. But this fix doesn't actually makes sense in many cases. For example if next works:

let a = Repository.fetch(56)

if you specify type explicity it will crash (in most cases):

let b = Repository.fetch<int, string>(56)

The case is that generic implementation should operate with generic types. If I understod correctly from what are you trying to do you should use OOP and polymophism when signature files are used to hide implementation aspects. For example:

namespace DataStorage

[<AbstractClass>]
type Repository<'TKey,'T>() =     
    abstract member put: 'T -> unit
    abstract member fetch: 'TKey -> 'T  
    abstract member remove: 'T -> unit

type IntRepository() =
    inherit Repository<int, int>()
    override self.put item = ()
    override self.fetch index = 5
    override self.remove item = ()

type StringRepository() =
    inherit Repository<int, string>()
    override self.put item = ()
    override self.fetch index = "Hello"
    override self.remove item = ()
petro.sidlovskyy
  • 5,075
  • 1
  • 25
  • 29
  • This makes sense. I'm trying to use signature files in the same way that I do in OCaml. I guess this is the standard way to do things in the .NET world. Thanks @petro.silovskyy for the time, thoughts, and explanation. – cameron Apr 18 '13 at 15:18
2

One (some what limiting) alternative that I've recently tried is sort of one step away from generics but seems to work for my scenario. Basically the signature file for the fetch function now looks like this.

'a -> RepositoryRecord

and the implementation of the RepositoryRecord is an algebraic datatype.

type RepositoryRecord = | SomeVal1 of int * string | SomeVal2 of string
cameron
  • 81
  • 6
  • I don't think this is limiting at all. I use this method all the time. In fact, I would go so far as to say this is idiomatic. – Shredderroy Apr 18 '13 at 23:33
  • @Shredderroy thanks for the comment. Its good to know that my idea is not totally wrong. ;) – cameron Apr 20 '13 at 20:34
  • sure thing. Perhaps someone more knowledgeable will weigh on, but in regard to your comment `...one step away from generics`, I just want to say that I don't believe your original approach involved `generics`. There is, after all, a difference between `generics` and `casting`, which is what I believe you would have had to end up doing at some point. – Shredderroy Apr 20 '13 at 23:04