5
votes

Given the following CRTP type in C#:

public abstract class DataProviderBase<TProvider>
    where TProvider : DataProviderBase<TProvider> { }

How would I get its generic type definition in F#?

let typeDef = typedefof<DataProviderBase<_>>

yields the error:

Type constraint mismatch when applying the default type 'DataProviderBase<'a>' for a type inference variable. The resulting type would be infinite when unifying ''a' and 'DataProviderBase<'a>' Consider adding further type constraints

In C#, it would be:

var typeDef = typeof(DataProviderBase<>);

UPDATE

I found a workaround:

[<AbstractClass>]
type DummyProvider() =
  inherit DataProviderBase<DummyProvider>()

let typeDef = typeof<DummyProvider>.BaseType.GetGenericTypeDefinition()

Is there another way to do it, without the extra type?

1

1 Answers

5
votes

I think this is actually a very good question. I didn't find a better workaround for this. You can slightly simplify your workaround by using typedefof like this:

let typeDef = typedefof<DataProviderBase<DummyProvider>>

TECHNICAL DETAILS

The problem is that F#'s typedefof<'T> is just an ordinary function that takes a type argument (unlike typeof in C#, which is an operator). In order to call it, you need to give it an actual type and the function will then call GetGenericTypeDefinition under the cover.

The reason why typedefof<option<_>> works is that F# specifies a default type as an argument (in this case obj). In general, F# chooses the less concrete type that matches the constraints. In your case:

DataProviderBase<_> will become DataProviderBase<DataProviderBase<_>> and so on.

Unless you define a new type (as in your workaround), there is no concrete type that could be used as a type argument of typedefof<...>. In this case, the defaulting mechanism simply doesn't work...