2013-04-05 9 views
17

F # ma funkcję o nazwie "Type extension", która daje programistom możliwość rozszerzenia istniejących typów. Dostępne są dwa typy rozszerzeń: wewnętrzne rozszerzenie i opcjonalne rozszerzenie. Pierwszy jest podobny do typów częściowych w języku C#, a drugi jest podobny do rozszerzenia metody (ale mocniejszy).Rozszerzenia typów i widoczność członków w języku F #

Aby użyć rozszerzenia wewnętrznego, należy umieścić dwie deklaracje w tym samym pliku. W tym przypadku kompilator połączy dwie definicje w jeden ostateczny typ (tj. Są to dwie "części" jednego typu).

Kwestia jest taka, że ​​te dwa rodzaje ma inne zasady dostępu dla różnych członków i wartości:

// SampleType.fs 
// "Main" declaration 
type SampleType(a: int) = 
    let f1 = 42 
    let func() = 42 

    [<DefaultValue>] 
    val mutable f2: int 
    member private x.f3 = 42 
    static member private f4 = 42 

    member private this.someMethod() = 
     // "Main" declaration has access to all values (a, f1 and func()) 
     // as well as to all members (f2, f3, f4) 
     printf "a: %d, f1: %d, f2: %d, f3: %d, f4: %d, func(): %d" 
      a f1 this.f2 this.f3 SampleType.f4 (func()) 

// "Partial" declaration 
type SampleType with 

    member private this.anotherMethod() = 
     // But "partial" declaration has no access to values (a, f1 and func()) 
     // and following two lines won't compile 
     //printf "a: %d" a 
     //printf "f1: %d" f1 
     //printf "func(): %d" (func()) 

     // But has access to private members (f2, f3 and f4) 
     printf "f2: %d, f3: %d, f4: %d" 
      this.f2 this.f3 SampleType.f4 

czytam F specyfikację #, ale nie znaleźliśmy żadnych pomysłów, dlaczego F # kompilator różnicują deklaracji wartości i członka.

W 8.6.1.3 section o f # specyfikacji że „Funkcje i wartości określone definicje przykład są lexically zawężona (a tym samym pośrednio prywatny) do określonego obiektu.”. Częściowa deklaracja ma dostęp do wszystkich prywatnych członków (statycznych i instancji). Domyślam się, że autorzy specyfikacji "zakresu leksykalnego" mają na myśli tylko "główną" deklarację, ale zachowanie to wydaje mi się dziwne.

Pytanie brzmi: czy to zachowanie jest zamierzone i jakie ma racjonalne uzasadnienie?

Odpowiedz

10

To jest świetne pytanie! Jak zauważyłeś, specyfikacja mówi, że "lokalne wartości są leksykalnie zakreślone do definiowanego obiektu", ale patrząc na specyfikację F #, w rzeczywistości nie definiuje to, co leksykalne oznacza w tym przypadku.

Jak pokazuje przykład, obecne zachowanie polega na tym, że leksykalny zakres definicji obiektu to tylko definicja głównego typu (z wyłączeniem rozszerzeń wewnętrznych). Nie jestem tym zbyt zaskoczony, ale widzę, że druga interpretacja też miałaby sens ...

Myślę, że dobrym tego powodem jest to, że dwa rodzaje rozszerzeń powinny zachowywać się tak samo (jak to tylko możliwe) i powinieneś być w stanie zrekompensować swój kod z korzystania z jednego do korzystania z drugiego, jak potrzebujesz. Te dwa rodzaje różnią się jedynie sposobem ich kompilacji pod osłoną. Ta właściwość byłaby zerwana, gdyby jeden rodzaj miał dostęp do zakresu leksykalnego, podczas gdy drugi nie (ponieważ, członkowie rozszerzenia technicznie nie mogą tego zrobić).

To powiedziawszy, myślę, że to może być (przynajmniej) wyjaśnione w specyfikacji. Najlepszym sposobem zgłoszenia tego problemu jest wysłanie wiadomości e-mail pod numer fsbugs pod adresem microsoft dot com.

+2

Dostałem twój punkt o podobieństwie między dwoma typami rozszerzeń typu. Ale zachowanie * wewnętrznych * i * opcjonalnych * rozszerzeń jest różne: opcjonalne rozszerzenia mogą uzyskiwać dostęp tylko do publicznej powierzchni rozszerzonego typu, ale wewnętrzne rozszerzenia nadal mają dostęp do prywatnych i chronionych członków rozszerzonego typu. Miały też inną semantyczną cechę, a nie tylko sposób ich kompilacji pod osłoną. –

+0

@SergeyTeplyakov Hmm, to dobry punkt. Masz rację, że różnią się pod tym względem. Przypuszczam, że kluczową kwestią jest zatem definicja "zakresu leksykalnego" w tym kontekście (czego wyraźnie brakuje w specyfikacji F #). –

+0

Dzięki, Tomas. Myślę, że masz rację co do zakresu leksykalnego. Chodzi mi o to, że częściowe typy w języku C# są dobrze znaną i szeroko stosowaną funkcją wsparcia projektantów. Ale z takim ograniczeniem nie możemy traktować wewnętrznego rozszerzenia jako w pełni funkcjonalnej deklaracji typu częściowego. A to oznacza, że ​​nie możemy uzyskać wsparcia projektantów (dla WinForms i WPF), dopóki wewnętrzne rozszerzenia nie mają takich ograniczeń. –

3

wysłałem to pytanie do fsbugs w microsoft kropką com i dostałem następującą odpowiedź od Don Syme:

Hi Sergey,

Tak, zachowanie jest zamierzone. Gdy używasz "let" w zakresie klasy, identyfikator ma zasięg leksykalny nad definicją typu. Wartość nie może nawet zostać umieszczona w polu - na przykład, jeśli wartość nie zostanie przechwycona żadnymi metodami, wówczas staje się lokalna dla konstruktora. Ta analiza jest przeprowadzana lokalnie na zajęciach.

Rozumiem, że można oczekiwać, że funkcja działa jak częściowe klasy w języku C#. Jednak to po prostu nie działa w ten sposób.

Myślę, że termin "zakres leksykalny" powinien być bardziej precyzyjnie określony w specyfikacji, ponieważ w przeciwnym razie obecne zachowanie byłoby zaskakujące również dla innych programistów.

Wielkie dzięki dla Dona za jego odpowiedź!

Powiązane problemy