Haskell Language
Buchi digitati
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.