Possiamo scrivere singoli, le istanze complete per i parametri di tipo *
:Pattern Matching sui Tipi Promosso
class MyClass d where
f :: d -> Int
instance MyClass (Maybe d) where
f _ = 3
test1 :: Maybe d -> Int
test1 x = f x
Questo compila bene, e notare che non abbiamo bisogno di un (MyClass (forse d)) vincolo test1 perché il compilatore trova un'istanza corrispondente per qualsiasi Maybe d
.
Possiamo anche scrivere un'istanza onnicomprensiva per i parametri di classe che sono costruttori di tipo:
class MyClass2 (a :: * -> *) where
g :: Tagged (a Int) Int
instance MyClass2 a where
g = Tagged 3
test2 :: Int
test2 = untag (g :: Tagged (Maybe Int) Int)
Questo compila anche bene, e test2 non ha bisogno di una (MyClass2 forse) vincolo, perché il compilatore trova un istanza corrispondente.
Si consideri il seguente codice giocattolo per calcolare la lunghezza di un (promosso) tipo di elenco:
{-# LANGUAGE TypeOperators,
DataKinds,
KindSignatures,
GADTs,
FlexibleInstances,
FlexibleContexts,
ScopedTypeVariables #-}
import Data.Tagged
import Data.Proxy
data HList :: [*] -> * where
HNil :: HList '[]
HCons :: a -> HList as -> HList (a ': as)
class ListLen a where
len :: Tagged a Int
instance ListLen (HList '[]) where
len = Tagged 0
instance (ListLen (HList as)) => ListLen (HList (a ': as)) where
len = Tagged (1+(untag (len :: Tagged (HList as) Int)))
test3 :: Int
test3 = untag (len :: Tagged (HList '[Int, Double, Integer]) Int)
test4 :: (Proxy (HList qs)) -> Int
test4 (_ :: Proxy (HList qs)) = untag (len :: Tagged (HList qs) Int) -- error occurs here
Ciò genera l'errore:
No instance for (ListLen (HList qs))
arising from a use of `len'
Possible fix: add an instance declaration for (ListLen (HList qs))
...
Se si commentano la firma per test4
, GHCi deduce il tipo come
test4 :: (ListLen (HList qs)) => (Proxy (HList qs)) -> Int
ma questo non dovrebbe essere necessario. Chiaramente, ho scritto istanze per ListLen
che corrispondono a qualsiasi tipo di elenco concepibile: un caso di 'lista vuota' e un caso 'contro'. Il problema rimane lo stesso se cambio il tipo del parametro ListLen
per avere il tipo [*]
(ad esempio, rimuovere il wrapper HList
in ListLen
e le sue istanze).
Se si commentano fuori test4
, test3
compila e funziona bene: da quando sono (essenzialmente) dato sintassi esplicita ('[Int,Double,Integer]
) per come ho costruito la lista tipo, il compilatore è stato in grado di trovare le istanze corrispondenti.
Sto tentando di scrivere codice che crea un elenco di tipi per me, quindi non dovrò scrivere la sintassi esplicita della lista di tipi. Tuttavia, sembra che l'uso della sintassi esplicita sia l'unico modo in cui GHC è in grado di abbinare queste istanze complete. Forse c'è un caso che mi manca? Sintassi che non sto usando?
Cosa posso fare per far capire a GHC che ho un'istanza per tutto il genere [*]
? Sto usando GHC 7.4.2. Questo potrebbe essere essere correlato a un previous post sulla decostruzione di tipi non promossi. È possibile trovare la promozione del tipo su here.
I suggerimenti del titolo sono i benvenuti. – crockeea
Hai * diverse * istanze, come sarebbe no quale? GHC deve selezionare l'istanza, e questa è un'operazione non parametrica, quindi richiede un vincolo. Non penso che quello che vuoi fare abbia senso. –
GHC può anche risolvere istanze * sovrapposte * (che non sono queste) in fase di esecuzione utilizzando un vincolo di classe esplicito (che verrebbe altrimenti dedotto). Perché non può vedere che c'è * sempre * una corrispondenza, anche se deve attendere il runtime per scegliere l'istanza appropriata? Certo, sto chiedendo di farlo * senza * il vincolo di classe esplicito (e gratuito), che non ho il lusso di fornire. – crockeea