Reputation: 2248
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
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