2015-03-11 10 views
5

Ho giocato con le istanze Applicative per capire come funzionano. Tuttavia, onestamente non capisco questo comportamento.In che modo GHCi stampa i valori parzialmente applicati creati da "puro"?

Se si definisce il proprio tipo di dati, quindi applicare pure ad esso senza altri argomenti, non viene stampato nulla, ma si verifica un errore se si tenta di applicare qualcosa al risultato.

ghci> data T = A 
ghci> pure A 
ghci> pure A 0 

<interactive>:21:1: 
    No instance for (Show T) arising from a use of ‘print’ 
    In a stmt of an interactive GHCi command: print it 

Tuttavia, se faccio T un'istanza di Show, poi A viene stampato in entrambi i casi.

ghci> data T = A deriving (Show) 
ghci> pure A 
A 
ghci> pure A 0 
A 

Quello che davvero non capisco è come pure A può essere un valore che viene stampato in modo diverso tra i due casi. pure A non viene applicato parzialmente?

Capisco perché chiamare gli errori pure A 0 nel primo esempio e non nel secondo, ha senso per me. Si sta utilizzando l'istanza ((->) r) di Applicative, quindi restituisce semplicemente una funzione che restituisce sempre A.

Ma in che modo è pure istanziato con un solo valore quando il tipo dell'applicativo stesso non è ancora noto? Inoltre, come può GHC stampare questo valore?

risposta

14

GHCi è un po 'particolare. In particolare, quando si digita un'espressione al prompt, si cerca di interpretare in due modi diversi, al fine di:

  1. Come IO azione da eseguire.
  2. Come valore da stampare.

Dal IO è Applicative, si sta interpretando pure A come produrre qualcosa IO azione di tipo T. Esegue quell'azione (che non fa nulla) e poiché il risultato non è in Show, non stampa nulla. Se rendi T un'istanza di Show, quindi stampa il risultato per te.

Quando si scrive pure A 0, GHCi vede questo:

pure :: Applicative f => a -> f a 
pure A :: Applicative f => f T 

E dal momento che si applica pure A-0, pure A deve essere una funzione a->b per alcuni tipi a e b, e a deve contenere 0.

(Num a, Applicative f) => f T ~ (a -> b) 

(Si noti che x ~ y significa che x e y unificare-che può essere fatto per avere lo stesso tipo.)

Così deve avere f ~ ((->) a) e T ~ b, quindi in realtà GHC deduce che, in questo contesto,

pure A :: Num a => ((->) a) T 

cui possiamo riscrivere come

pure A :: Num a => a -> T 

Bene, (->) a è un'istanza di Applicative, vale a dire "lettore", quindi questo è ok. Quando applichiamo pure A a 0 otteniamo qualcosa di tipo T, ovvero A. Questo non può essere interpretato come come un'azione IO, quindi se T non è un'istanza di Show, GHCi si lamenterà.

7

Quando si assegna un valore di tipo ambiguo al prompt GHCi per la valutazione, si tenta di impostare il tipo in modo predefinito in vari modi. In particolare, prova se può adattarsi a un tipo IO a, nel caso in cui si desideri eseguire un'azione IO (vedere the GHC manual). Nel tuo caso, pure A imposta automaticamente il tipo IO T. Inoltre:

Inoltre, GHCi stamperà il risultato dell'azione di I/O se (e solo se):

  • il tipo di risultato è un'istanza di Show.
  • Il tipo di risultato non è ().
+0

Perché proprio "IO"? È solo un default arbitrario? –

+1

@AlexisKing, è un valore predefinito per il tipo di sviluppo interattivo che le persone tendono a fare con GHCi. Non vi è alcun 'main' in GHCi, e sarebbe fastidioso dover invocare qualche comando di interprete speciale per eseguire un'azione' IO'. – dfeuer

+1

Con azioni 'IO' e solo azioni' IO', * le esegue * oltre a valutare, quindi devono essere trattate in modo speciale. Il fatto che anche le azioni generiche di "Applicative/Monad" siano predefinite è probabilmente solo un bell'effetto collaterale, anche se è esplicitamente menzionato nel manuale. –

Problemi correlati