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ń.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow