Haskell Language
Typowane otwory
Szukaj…
Uwagi
Jedną z mocnych stron Haskell jest możliwość wykorzystania systemu typów do modelowania części domeny problemów w systemie typów. W ten sposób często napotyka się bardzo złożone typy. Pisząc programy z tymi typami (tj. Z wartościami posiadającymi te typy), czasami żonglowanie wszystkimi typami staje się prawie niemożliwe. Począwszy od GHC 7.8, pojawiła się nowa funkcja składniowa o nazwie dziury maszynowe. Wpisane dziury nie zmieniają semantyki języka podstawowego; są one przeznaczone wyłącznie jako pomoc w pisaniu programów.
Szczegółowe wyjaśnienie wpisywanych otworów, a także omówienie projektu wpisywanych otworów można znaleźć na wiki Haskell .
Część instrukcji obsługi GHC na temat wpisywanych otworów .
Składnia wpisanych otworów
Wpisany otwór to pojedynczy znak podkreślenia ( _
) lub prawidłowy identyfikator Haskell, który nie jest objęty zakresem, w kontekście wyrażenia. Przed istnieniem wpisywanych dziur obie te rzeczy spowodowałyby błąd, więc nowa składnia nie koliduje z żadną starą składnią.
Kontrolowanie zachowania wpisywanych otworów
Domyślne zachowanie wpisywanych otworów polega na generowaniu błędu czasu kompilacji podczas napotkania wpisanego otworu. Istnieje jednak kilka flag, które pozwalają dostosować ich zachowanie. Te flagi są podsumowane w następujący sposób ( GHC trac ):
Domyślnie GHC ma włączone dziurki na maszynie i generuje błąd kompilacji, gdy napotka dziurkę na klawiaturze.
Gdy włączona jest opcja
-fdefer-type-errors
-fdefer-typed-holes
lub-fdefer-typed-holes
, błędy dziury są konwertowane na ostrzeżenia i podczas oceny powodują błędy w czasie wykonywania.Flaga ostrzegawcza
-fwarn-typed-holes
jest domyślnie włączona. Bez-fdefer-type-errors
lub-fdefer-typed-holes
ta flaga nie działa, ponieważ wpisane otwory są błędem w tych warunkach. Jeśli którakolwiek z flag odroczenia jest włączona (konwersja błędów wpisywanych otworów w ostrzeżenia), flaga-fno-warn-typed-holes
wyłącza ostrzeżenia. Oznacza to, że kompilacja po cichu się powiedzie, a ocena dziury spowoduje błąd w czasie wykonywania.
Semantyka dziur maszynowych
Wartość otworu typu można po prostu powiedzieć, że jest undefined
, chociaż wpisany otwór powoduje błąd czasu kompilacji, więc przypisanie mu wartości nie jest absolutnie konieczne. Jednak wpisany otwór (gdy są włączone) powoduje błąd czasu kompilacji (lub ostrzeżenie z błędami typu odroczonego), który podaje nazwę wpisanego otworu, jego wywnioskowany najbardziej ogólny typ i typy wszelkich lokalnych powiązań. Na przykład:
Prelude> \x -> _var + length (drop 1 x)
<interactive>:19:7: Warning:
Found hole `_var' with type: Int
Relevant bindings include
x :: [a] (bound at <interactive>:19:2)
it :: [a] -> Int (bound at <interactive>:19:1)
In the first argument of `(+)', namely `_var'
In the expression: _var + length (drop 1 x)
In the expression: \ x -> _var + length (drop 1 x)
Należy pamiętać, że w przypadku otworów w wyrażeniach wpisywanych wszedł na repl GHCi (jak wyżej), rodzaj ekspresji wprowadzono również podano, jak it
(tutaj typu [a] -> Int
).
Użycie wpisanych otworów do zdefiniowania instancji klasy
Wpisane otwory mogą ułatwić definiowanie funkcji poprzez interaktywny proces.
Załóżmy, że chcesz zdefiniować Foo Bar
instancji klasy (dla niestandardowego typu Bar
, aby użyć go z jakąś funkcją biblioteki polimorficznej, która wymaga instancji Foo
). Teraz tradycyjnie przeglądasz dokumentację Foo
, zastanawiasz się, które metody musisz zdefiniować, analizujesz ich typy itp. - Ale przy wpisywanych dziurach możesz to pominąć!
Najpierw po prostu zdefiniuj fikcyjną instancję:
instance Foo Bar where
Kompilator będzie teraz narzekał
Bar.hs:13:10: Warning:
No explicit implementation for
‘foom’ and ‘quun’
In the instance declaration for ‘Foo Bar’
Ok, więc musimy zdefiniować foom
dla Bar
. Ale co to w ogóle powinno być? Ponownie jesteśmy zbyt leniwi, aby zaglądać do dokumentacji i po prostu zapytaj kompilatora:
instance Foo Bar where
foom = _
Wykorzystaliśmy tutaj wpisaną dziurę jako proste „zapytanie dotyczące dokumentacji”. Dane wyjściowe kompilatora
Bar.hs:14:10:
Found hole ‘_’ with type: Bar -> Gronk Bar
Relevant bindings include
foom :: Bar -> Gronk Bar (bound at Foo.hs:4:28)
In the expression: _
In an equation for ‘foom’: foom = _
In the instance declaration for ‘Foo Bar’
Uwaga jak kompilator już wypełniony zmienną typu klasa z typem beton Bar
że chcemy wystąpienia go. Może to znacznie ułatwić zrozumienie podpisu w porównaniu z polimorficznym znalezionym w dokumentacji klasy, zwłaszcza jeśli masz do czynienia z bardziej skomplikowaną metodą, np. Klasą typu wieloparametrowego.
Ale czym do diabła jest Gronk
? W tym momencie prawdopodobnie warto zapytać Hayoo . Możemy jednak bez tego uciec: zakładamy, że jest to nie tylko konstruktor typu, ale także konstruktor pojedynczej wartości, tzn. Można go użyć jako funkcji, która w jakiś sposób wytworzy wartość Gronk a
. Więc próbujemy
instance Foo Bar where
foom bar = _ Gronk
Jeśli mamy szczęście, Gronk
jest w rzeczywistości wartością, a kompilator powie teraz
Found hole ‘_’
with type: (Int -> [(Int, b0)] -> Gronk b0) -> Gronk Bar
Where: ‘b0’ is an ambiguous type variable
Ok, to brzydkie - najpierw zauważ, że Gronk
ma dwa argumenty, więc możemy udoskonalić naszą próbę:
instance Foo Bar where
foom bar = Gronk _ _
A teraz jest to całkiem jasne:
Found hole ‘_’ with type: [(Int, Bar)]
Relevant bindings include
bar :: Bar (bound at Bar.hs:14:29)
foom :: Bar -> Gronk Bar (bound at Foo.hs:15:24)
In the second argument of ‘Gronk’, namely ‘_’
In the expression: Gronk _ _
In an equation for ‘foom’: foom bar = Gronk _ _
Możesz teraz dalej robić postępy, np. Dekonstruując wartość bar
(komponenty pojawią się wraz z typami w sekcji Relevant bindings
). Często w pewnym momencie jest zupełnie oczywiste, jaka będzie poprawna definicja, ponieważ widzisz wszystkie dostępne argumenty, a typy pasują do siebie jak układanka. Lub alternatywnie możesz zobaczyć, że definicja jest niemożliwa i dlaczego.
Wszystko to działa najlepiej w edytorze z interaktywną kompilacją, np. Emacs z trybem haskell. Następnie możesz użyć wpisywanych otworów, podobnie jak zapytania o wartość myszy na IDE, dla interpretowanego dynamicznego języka imperatywnego, ale bez wszystkich ograniczeń.