2017-01-19 42 views
6

Mam klasę ogólną w języku F # z jednym parametrem typu i chciałbym utworzyć klasę statyczną zawierającą metody fabryczne. Kiedy piszę moje klasy, kompilator F # generuje błąd związany ze "zmienną typu uciekającą z jej zasięgu". Moje pytanie brzmi: dlaczego istnieje błąd i jak go naprawić.Zakres zmiennoprzepustów typu przy łączeniu ogólnej i nietypowej klasy

Utworzyłem fragment minimalnej wielkości wykazujące problem:

type Foo<'a>(element : 'a) = 

    member this.Copy() = Bar.Create(element) 

and Bar = 

    static member Create(element : 'a) = new Foo<'a>(element) 

Wzajemne rekurencji w rodzajach tam jest, bo chciałbym typ Foo<'a> aby móc wywołać metody fabryka w klasa statyczna. Powyższy fragment nie jest kompilowany, a błąd jest następujący: "Wnioskowanie typów spowodowało, że zmienna typu a wymknie się jej zakresowi. Rozważ dodanie jawnej deklaracji parametru typu lub dostosowanie kodu, aby było mniej ogólne." Błąd jest rejestrowany jako znajdujący się w metodzie Create klasy Bar. Niestety, tak naprawdę nie rozumiem problemu ani go naprawić. Jakieś pomysły?

Oto dodatkowa obserwacja. Skrótowa kompilacja zostaje skompilowana. Wydaje się więc, że problem związany jest z wnioskiem o typ, dokonanym na podstawie metody Copy() klasy Foo<'a>. Ponadto fragment

type Foo<'a>(element : 'a) = 

    member this.Copy() = Bar.Create(element) 

and Bar = 

    static member Create<'a>(element) = new Foo<'a>(element) 

jest bardziej C# -jak wersji kodu (gdzie metoda statyczna wyraźnie jest wykonany ogólne), które również nie kompiluje, z błędem „Ten kod nie jest wystarczająco rodzajowy. zmienna typu "a nie może zostać uogólniona, ponieważ wymknie się jej zakresowi."

Odpowiedz

6

Typ interferencji dla członków rekurencyjnych często wymaga adnotacji typu na co najmniej niektórych definicjach. Jednak czasami można tego uniknąć poprzez definicje ponownego zamawiania, jak można w co najmniej swojej uproszczonej Repro:

type Bar = 
    static member Create(element) = Foo(element) 
and Foo<'a>(element:'a) = 
    member this.Copy() = Bar.Create(element) 

(zauważ, że ja nawet usunięte adnotacji na element w Bar.Create).

Nie wiem, że istnieje łatwe do zrozumienia wyjaśnienie, jakie adnotacje będą potrzebne w konkretnej sytuacji, niestety.

2

Jestem rzeczywiście widząc inny błąd, o typ zmiennej 'a jest nierozwiązany i mogę obejść przez parametryzacji bar z 'a:

type Foo<'a>(element : 'a) = 
    member this.Copy() = Bar.Create(element) 

and Bar<'a> = 
    static member Create(element : 'a) = new Foo<'a>(element) 

Obawiam się, że nie mają bardzo dobre wyjaśnienie, dlaczego jest to wymagane w scenariuszu, w którym występują typy rekurencyjne, a nie w przypadku oddzielnego typu: Bar.

Staram się unikać typów rekurencyjnych - sytuacje, w których nie można się bez nich obejść, są rzadkością. W większości przypadków możesz zrestrukturyzować kod, aby uniknąć rekursji, i zwykle kończy się na czymś, co jest łatwiejsze do odczytania i jeśli to konieczne, może się zrestrukturyzować.

+2

Uzgodniono, że nie można dopuścić do tego, aby wzajemnie rekurencyjne typy były trzymane w napięciu. –

4

To wydaje się działać bez Bar rodzajowe:

type Foo<'a>(element : 'a) = 
    member this.Copy() = Bar.Create element 
and Bar = 
    static member Create<'a>(element : 'a) : Foo<'a> = Foo(element) 

Online Demo

Nie mam pojęcia dlaczego, po prostu okazało się metodą prób i błędów.

+1

To dziwne. Być może istnieje pewien związek pomiędzy zmiennymi typu w metodzie 'Foo' i' Create', ponieważ wynik 'Create' jest interpretowany jako' Foo <'a> 'bez adnotacji. Nie mogę uwierzyć, że oczekuje się, że będzie działać w ten sposób. – scrwtp