Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# generic type constraints and duck typing

I'm trying to implement duck typing in F# and I spotted that you can have a member constraint in F# generics as follows:

type ListEntryViewModel<'T when 'T : (member Name : string)>(model:'T) = 
  inherit ViewModelBase()

  member this.Name with get() = model.Name

However, the above code won't compile when I try to reference the property. I get a compiler error:

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

Is it possible to implement duck typing via a generic constraint?

like image 689
Noel Kennedy Avatar asked Oct 22 '12 12:10

Noel Kennedy


2 Answers

There was a similar question recently where member constraints were used in the type declaration.

I'm not sure how to correct your sample to make it compile, but I would not be surprised if that was not possible. Member constraints are designed to be used with statically resolved type parameters and especially with inline functions or members and I do not think it is idiomatic F# code to use them with type parameters of a class.

I think that a more idiomatic solution to your example would be to define an interface:

type INamed = 
  abstract Name : string

type ListEntryViewModel<'T when 'T :> INamed>(model:'T) =  
  member this.Name = model.Name

(In fact, the ListEntryViewModel probably does not need a type parameter and can just take INamed as a constructor parameter, but there may be some benefit in writing it in this way.)

Now, you can still use duck typing and use ListEntryViewModel on things that have Name property, but do not implement the INamed interface! This can be done by writing an inline function that returns INamed and uses static member constraints to capture the existing Name property:

let inline namedModel< ^T when ^T : (member Name : string)> (model:^T)= 
  { new INamed with
      member x.Name = 
        (^T : (member Name : string) model) }

You can then create your view model by writing ListEntryViewModel(namedModel someObj) where someObj does not have to implement the interface, but needs just the Name property.

I would prefer this style, because by taking an interface, you can better document what you require from the model. If you have other objects that do not fit the scheme, you can adapt them, but if you're writing a model, then implementing an interface is a good way to make sure it exposes all the required functionality.

like image 178
Tomas Petricek Avatar answered Oct 22 '22 15:10

Tomas Petricek


Is it possible to implement duck typing via a generic constraint?

No. Except for a few special cases F# implements only nominal typing where duck typing is not possible. As the other answers have explained, the idiomatic "solution" is to retrofit an interface onto all of the classes that you wish had adhered to that interface but, of course, that is impractical in most cases where you want duck typing.

Note that this limitation in F# is inherited from .NET. If you want to see a more practical solution akin to duck typing, check out OCaml's structurally typed polymorphic variants and objects.

like image 28
J D Avatar answered Oct 22 '22 13:10

J D