2012-10-22 7 views
7

Próbuję zaimplementować kaczka wpisywanie w F # i dostrzegłem, że można mieć member constraint in F# generics następująco:F # ograniczenia typu rodzajowego i kaczka wpisując

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

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

Jednakże powyższy kod nie będzie kompilować, kiedy spróbuj odwołać się do właściwości. Pojawia się błąd kompilatora:

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.

Czy można wprowadzić typowanie kaczkowe za pomocą ogólnego ograniczenia?

+0

Należy zauważyć, że nie jest to naprawdę "pisanie na maszynie", ale raczej pisanie strukturalne (pod). – Eyvind

Odpowiedz

19

Ostatnio było podobne pytanie, gdzie member constraints were used in the type declaration.

Nie jestem pewien, jak poprawić próbkę, aby ją skompilować, ale nie byłbym zaskoczony, gdyby to było niemożliwe. Więzy członkowskie są zaprojektowane do użycia z parametrami statycznie rozdzielonymi, a zwłaszcza z funkcjami lub członkami inline i nie uważam, że jest to fikcyjny kod F #, który używałby ich z parametrami typu klasy.

myślę, że bardziej idiomatyczne rozwiązanie do przykładu byłoby zdefiniowanie interfejsu:

type INamed = 
    abstract Name : string 

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

(W rzeczywistości, ListEntryViewModel prawdopodobnie nie potrzebujesz parametr typu i może po prostu wziąć INamed jako parametr konstruktora , ale mogą istnieć pewne korzyści w formie pisemnej go w ten sposób.)

teraz można nadal korzystać z kaczki pisanie i użyć ListEntryViewModel na rzeczy, które mają Name nieruchomość, ale nie implementować interfejs INamed! Można to zrobić pisząc inline funkcję zwracającą INamed i używa ograniczeń członków statycznych uchwycić istniejące Name właściwość:

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

Następnie można utworzyć widoku modelu pisząc ListEntryViewModel(namedModel someObj) gdzie someObj nie musi implementować interfejs , ale potrzebuje tylko własności Name.

Wolałbym ten styl, ponieważ dzięki zastosowaniu interfejsu można lepiej dokumentować to, czego wymaga się od modelu. Jeśli masz inne obiekty, które nie pasują do schematu, możesz je dostosować, ale jeśli piszesz model, implementacja interfejsu jest dobrym sposobem, aby upewnić się, że eksponuje wszystkie wymagane funkcje.

5

Aby dokonać oryginalną pracę kod:

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

    member inline this.Name with get() = (^T : (member Name : string) model) 

Więc trzeba oznaczyć element jako „inline” i powtórzyć ograniczenia w funkcji składowej.

Zgadzam się z Tomasem, że podejście oparte na interfejsie jest zwykle preferowane w F #.

6

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

Nie. Poza kilkoma wyjątkowymi przypadkami F # implementuje tylko typowe pisanie, gdy pisanie na kaczkach nie jest możliwe. Jak wyjaśniono w innych odpowiedziach, idiomatyczne "rozwiązanie" polega na doposażeniu interfejsu we wszystkie klasy, które chciałeś zastosować w tym interfejsie, ale w większości przypadków jest to niepraktyczne, gdy chcesz pisać na maszynie.

Należy zauważyć, że to ograniczenie w F # jest dziedziczone z .NET.Jeśli chcesz zobaczyć bardziej praktyczne rozwiązanie przypominające pisanie kaczkami, sprawdź strukturalnie typowane polimorficzne warianty i obiekty OCamla.

+0

+1, aby uzyskać praktyczną poradę. – missingfaktor