Ricerca…


Osservazioni

Uno dei punti di forza di Haskell è la capacità di sfruttare il sistema di tipi per modellare parti del dominio problematico nel sistema di tipi. Nel fare ciò, si incontrano spesso tipi molto complessi. Quando si scrivono programmi con questi tipi (cioè con valori con questi tipi), talvolta diventa quasi impossibile per "manipolare" tutti i tipi. A partire da GHC 7.8, c'è una nuova funzione sintattica chiamata buchi digitati. I buchi digitati non cambiano la semantica del linguaggio principale; sono intese puramente come un aiuto per scrivere programmi.

Per una spiegazione approfondita dei fori digitati, nonché una discussione sulla progettazione dei fori digitati, vedere il wiki Haskell .


Sezione della guida dell'utente GHC sui fori dattilografati .

Sintassi dei fori digitati

Un buco tipizzato è un singolo carattere di sottolineatura ( _ ) o un identificatore Haskell valido che non è nell'ambito, in un contesto di espressione. Prima dell'esistenza dei buchi digitati, entrambe queste cose avrebbero generato un errore, quindi la nuova sintassi non interferisce con alcuna sintassi precedente.

Controllo del comportamento dei fori digitati

Il comportamento predefinito dei buchi digitati è quello di produrre un errore in fase di compilazione quando si incontra un buco tipizzato. Tuttavia, ci sono diversi flag per mettere a punto il loro comportamento. Queste bandiere sono riassunte come segue ( GHC trac ):

Per impostazione predefinita, GHC ha inserito i fori abilitati e genera un errore di compilazione quando incontra un buco tipizzato.

Quando è -fdefer-type-errors o -fdefer-typed-holes , gli errori hole vengono convertiti in warning e generano errori di runtime quando vengono valutati.

Il flag di avviso -fwarn-typed-holes è -fwarn-typed-holes per impostazione predefinita. Senza -fdefer-type-errors o -fdefer-typed-holes questo flag è un no-op, poiché i buchi digitati sono un errore in queste condizioni. Se uno dei flag di defer è abilitato (convertendo gli errori del foro digitato in warning) il -fno-warn-typed-holes disabilita gli avvertimenti. Ciò significa che la compilazione riesce silenziosamente e la valutazione di un buco produrrà un errore di runtime.

Semantica di buchi digitati

Il valore di un foro di tipo può semplicemente essere undefined , sebbene un foro tipizzato inneschi un errore in fase di compilazione, quindi non è strettamente necessario assegnargli un valore. Tuttavia, un buco tipizzato (quando sono abilitati) produce un errore di compilazione (o avviso con errori di tipo posticipato) che indica il nome del foro digitato, il suo tipo più generale inferito e i tipi di collegamenti locali. Per esempio:

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)

Si noti che, nel caso di fori digitati nelle espressioni inseriti nel repl GHCi (come sopra), è entrato anche riportato il tipo di espressione, come it (qui di tipo [a] -> Int ).

Utilizzo di fori digitati per definire un'istanza di classe

I fori tipizzati possono facilitare la definizione delle funzioni, attraverso un processo interattivo.

Supponi di voler definire un'istanza di classe Foo Bar (per il tuo tipo di Bar personalizzato, per poterlo utilizzare con una funzione di libreria polimorfica che richiede un'istanza di Foo ). Ora dovresti cercare tradizionalmente la documentazione di Foo , capire quali metodi hai bisogno di definire, scrutare i loro tipi ecc. - ma con i buchi digitati, puoi davvero saltarlo!

Per prima cosa basta definire un'istanza fittizia:

instance Foo Bar where

Il compilatore ora si lamenterà

Bar.hs:13:10: Warning:
No explicit implementation for
  ‘foom’ and ‘quun’
In the instance declaration for ‘Foo Bar’

Ok, quindi dobbiamo definire foom per Bar . Ma che cosa è che anche dovrebbe essere? Ancora una volta siamo troppo pigri per cercare nella documentazione, e basta chiedere al compilatore:

instance Foo Bar where
  foom = _

Qui abbiamo usato un buco tipizzato come una semplice "query di documentazione". Le uscite del compilatore

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’

Si noti come il compilatore ha già riempito la variabile di tipo classe con il tipo concreto Bar che vogliamo istanziare per. Ciò può rendere la firma molto più semplice da capire rispetto a quella polimorfica trovata nella documentazione di classe, specialmente se si ha a che fare con un metodo più complicato, ad esempio una classe di tipo a più parametri.

Ma cosa diavolo è Gronk ? A questo punto, probabilmente è una buona idea chiedere a Hayoo . Tuttavia possiamo ancora cavarcela senza che: come ipotesi cieca, assumiamo che questo non sia solo un costruttore di tipi, ma anche il costruttore del valore singolo, cioè possa essere usato come una funzione che in qualche modo produrrà Gronk a valore Gronk a . Quindi proviamo

instance Foo Bar where
  foom bar = _ Gronk

Se siamo fortunati, Gronk è in realtà un valore, e il compilatore ora dirà

    Found hole ‘_’
      with type: (Int -> [(Int, b0)] -> Gronk b0) -> Gronk Bar
    Where: ‘b0’ is an ambiguous type variable

Ok, questo è brutto - inizialmente si noti che Gronk ha due argomenti, quindi possiamo affinare il nostro tentativo:

instance Foo Bar where
  foom bar = Gronk _ _

E questo ora è abbastanza chiaro:

    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 _ _

Ora puoi progredire ulteriormente, ad esempio decostruendo il valore della bar (i componenti verranno quindi visualizzati, con i tipi, nella sezione Relevant bindings ). Spesso, a un certo punto è del tutto ovvio quale sarà la definizione corretta, perché vedi tutti gli argomenti disponibili ei tipi combaciano come un puzzle. In alternativa, potresti vedere che la definizione è impossibile e perché.

Tutto ciò funziona al meglio in un editor con compilation interattiva, ad esempio Emacs con haskell-mode. È quindi possibile utilizzare fori tipizzati come query di valore di mouse over in un IDE per un linguaggio imperativo dinamico interpretato, ma senza tutte le limitazioni.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow