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?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With