F# generic type constraints and duck typing

后端 未结 3 1043
心在旅途
心在旅途 2020-12-19 00:21

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 wh         


        
相关标签:
3条回答
  • 2020-12-19 00:52

    To make your original code work:

    type ListEntryViewModel< ^T when ^T : (member Name : string)>(model:^T) = 
        inherit ViewModelBase()
    
        member inline this.Name with get() = (^T : (member Name : string) model)
    

    So you have to mark the member as "inline" and repeat the constraint in the member function.

    I agree with Tomas that an Interface-based approach is usually preferred in F#.

    0 讨论(0)
  • 2020-12-19 00:58

    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.

    0 讨论(0)
  • 2020-12-19 01:00

    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.

    0 讨论(0)
提交回复
热议问题