2015-09-08 16 views
11

Sto cercando di capire GHC.TypeLits e in particolare someNatVal. Capisco il modo in cui è usato in questo blog post here, ma come detto lo stesso esempio avrebbe potuto essere implementato utilizzando natVal, come:In GHC.TypeLits a cosa serve NaNVal (cosa che non è possibile raggiungere con natVal)?

isLength :: forall len a. KnownNat len => Integer -> List len a -> Bool 
isLength n _ = n == natVal (Proxy :: Proxy len) 

Esistono usi di someNatVal che non possono essere riscritti con natVal?

risposta

3

L'utilizzo principale di someNatVal sta utilizzando un valore in fase di esecuzione come se fosse un tipo sconosciuto in fase di compilazione.

La mia risposta a Can I have an unknown KnownNat? fornisce un esempio davvero stupido. Esistono milioni di modi migliori per scrivere un programma che faccia la stessa cosa di quello. Ma mostra cosa fa someNatVal. È essenzialmente una funzione dal valore al tipo - con una limitazione esistenziale che limita l'ambito di quel tipo a luoghi in cui non è staticamente noto.

+0

Non capisco il tuo esempio o quello che intende mostrare. Quello che sta succedendo non sta succedendo qui: 'let b = Bar" foo ":: Bar n in show b'? – jberryman

+0

@jberryman La parte importante è da cui proviene 'n'. In che modo il tipo 'n' si avvicina esattamente al valore fornito in fase di esecuzione? I tipi esistono solo al momento della compilazione in Haskell, dopo tutto. * Questo * è ciò che 'someNatVal' fa. – Carl

4

SomeNat è esistenzialmente quantificata:

data SomeNat = forall n. KnownNat n => SomeNat (Proxy n) 

che si legge come SomeNat contiene, beh, "alcuni n :: Nat". Proxy è un singleton che consente di sollevare quello n a livello di tipo per soddisfare il sistema di tipi - in un linguaggio tipizzato tipicamente abbiamo bisogno di tale costruzione molto raramente. Possiamo definire SomeNat in modo più esplicito utilizzando GADTs:

data SomeNat where 
    SomeNat :: forall n. KnownNat n => Proxy n -> SomeNat 

Così SomeNat contiene un Nat, che non è noto staticamente.

Poi

someNatVal :: Integer -> Maybe SomeNat 

legge come "someNatVal riceve un Integer e Maybe restituisce un hiden Nat". Non possiamo restituire uno KnownNat, perché Known significa "noto a livello di testo", ma non c'è nulla che sappiamo di un arbitrario Integer a livello di testo.

L'inverso (modulo SomeNat involucro ed un proxy invece del Proxy) di someNatVal è

natVal :: forall n proxy. KnownNat n => proxy n -> Integer 

si legge come "natVal riceve qualcosa che hanno un Nat nel suo genere e restituisce che Nat convertito in Integer".

I tipi di dati quantificati in modo approssimativo sono in genere used per il wrapping dei valori di runtime. Supponi di voler leggere uno Vec (un elenco con una lunghezza nota staticamente) da STDIN, ma come calcoli staticamente la lunghezza dell'input? Non c'è alcun modo. Quindi, avvolgi una lista, che hai letto, nel corrispondente tipo di dati quantificati esistenzialmente, dicendo "Non conosco la lunghezza, ma ne esiste una".

Tuttavia è eccessivo in molti casi. Per fare qualcosa con il tipo di dati quantificato esistenzialmente devi essere generale: ad es.se hai bisogno di trovare la somma di elementi di un SomeVec devi definire sumVec per Vec s con lunghezza arbitraria. Quindi scartiamo lo SomeVec e applichiamo sumVec, dicendo "Non conosco la lunghezza di uno Vec incartato, ma a sumVec non interessa". Ma invece di questo wrapping-unwrapping puoi usare directly CPS.

Tuttavia a causa di questa generalità è necessario abilitare ImpredicativeTypes per poter definire, ad esempio, un elenco con tali continuazioni. In tal caso, un elenco con dati quantificati in modo esistenziale è un common pattern che consente di aggirare lo ImpredicativeTypes.

Per quanto riguarda il tuo esempio, con propriamente definito Nat s, una versione che mette a confronto Nat s è lazier, che una versione che mette a confronto Integer s.