LA.27
LA.27

Reputation: 2248

F# static member constraints combined with IDisposable

I'd like to implement a generic F# class whose type parameter for sure provides a static method called "TryParse". Apart from that I want my class to get disposed correctly after not needed anymore. I've come up with the following implementation:

type Listener<'a when ^a : (static member TryParse : string -> ^a option)>() =
   // construct the object here
   let input : string = "" // get input
   let res = (^a : (static member TryParse : string -> ^a option) input)

   member this.Start() =
       // ...
       ()

   interface IDisposable with
      member this.Dispose() =
         // do cleanup
         ()

The thing is: on both of the members ("Start" and "Dispose") I get the following error:

Error: This code is not sufficiently generic. The type variable  ^a when  ^a : (static member TryParse : string -> ^a option) could not be generalized because it would escape its scope.

I can fix it on Start() member by decorating it with "inline", but there's no way I can do the same with the interface definition.

Is it possible to both enforce my generic type to implement a static method and define the class Disposable ?

Upvotes: 4

Views: 171

Answers (1)

Tomas Petricek
Tomas Petricek

Reputation: 243126

As mentioned in the comments, classes cannot have statically resolved type parameters. If you want to do something like this, a good trick is to have an inline method that has the constraints and captures the operations that you'll later need in an interface or as a first-class function.

In your case, you can change your class to take tryParse : string -> 'a option as an argument and then have a static method that will let you automatically capture this for types that support it:

type Listener<'a>(tryParse : string -> 'a option) =
   let input : string = "" 
   let res = tryParse input

   member this.Start() = ()

   interface System.IDisposable with
      member this.Dispose() = ()

The non-generic type with a static inline member would then be:

type Listener = 
  static member inline Create< ^b 
      when ^b : (static member TryParse : string -> ^b option)>() = 
    new Listener< ^b >(fun input -> 
      (^b : (static member TryParse : string -> ^b option) input))

Assuming you have a type Foo with appropriate TryParse member, you can write:

let l = Listener.Create<Foo>()

Upvotes: 6

Related Questions