Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get generic type definition for CRTP type

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?

like image 432
Daniel Avatar asked Jul 21 '11 21:07

Daniel


People also ask

What is generic type definition in c#?

A generic type definition is a template from which other types can be constructed. For example, from the generic type definition G<T> (expressed in C# syntax; G(Of T) in Visual Basic or generic <typename T> ref class G in C++) you can construct and instantiate the type G<int> ( G(Of Integer) in Visual Basic).


1 Answers

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...

like image 139
Tomas Petricek Avatar answered Oct 18 '22 01:10

Tomas Petricek