2015-12-01 2 views
5

Mi chiedo se non ci sia una ragione più profonda per cui non possiamo astrarre le classi di tipi (o possiamo?).Potremmo astrarre oltre le classi di tipi?

Per esempio, quando abbiamo

fzip :: (forall a.[a] -> [a]) -> [b] -> [c] -> [(b,c)] 
fzip f xs ys = zip (f xs) (f ys) 

allora possiamo dire

fzip (drop 42) [1..100] ['a'..'z'] 
fzip reverse [1..100] ['a'..'z'] 

e così via. Ma non possiamo

fzip (map succ) [1..100] ['a'..'z'] 

che possiamo risolvere con:

ezip :: (Enum b, Enum c) => (forall a.Enum a => [a] -> [a]) -> [b] -> [c] -> [(b,c)] 
ezip f xs ys = zip (f xs) (f ys) 

e allo stesso modo siamo in grado di risolvere

fzip (map (5*)) [1..100] [1.5, 2.3, 4.7] 

con

nzip :: (Num b, Num c) => (forall a.Num a => [a] -> [a]) -> [b] -> [c] -> [(b,c)] 
nzip f xs ys = zip (f xs) (f ys) 

Ma non è imbarazzante che abbiamo non può sussumere lo ezip e nzip con qualcosa di simile:

gzip :: (g b, g c) => (forall a. g a => [a] -> [a]) -> [b] -> [c] -> [(b,c)] 

se il codice è assolutamente identica, fino al nome della classe? Oppure possiamo in qualche modo?

È interessante notare che quando le istanze erano solo record che contenevano funzioni, ciò sarebbe facilmente possibile.

+1

Il sistema 'Constraint' di GHC si aspetta che la risolvibilità sia * decidibile in modo monomorfico (nel senso che tutti i tipi con tipo' Constraint' sono previsti da uno o più tipi _known_ - cioè, una classe superiore di typeclass non può essere variadic nell'intestazione typeclass) , Penso che potrebbe essere un vantaggio –

risposta

11

È possibile quasi farlo con ConstraintKinds:

{-# LANGUAGE ConstraintKinds, RankNTypes #-} 

import Data.Proxy 

gzip :: (g b, g c) => Proxy g -> (forall a . g a => [a] -> [a]) -> [b] -> [c] -> [(b,c)] 
gzip _ f xs ys = zip (f xs) (f ys) 

test1 = gzip (Proxy :: Proxy Enum) (map succ) [1 .. 100] ['a' .. 'z'] 
test2 = gzip (Proxy :: Proxy Num) (map (5*)) [1 .. 100] [1.5, 2.3, 4.7] 

La differenza principale è che è necessario l'argomento Proxy, perché GHC è in grado di dedurre l'istanza giusta per g senza aiuto.

8

Aggiungi un argomento Proxy per il vincolo:

{-# LANGUAGE PartialTypeSignatures #-} 

import Data.Proxy 

gzip :: (g b, g c) => Proxy g -> (forall a. g a => [a] -> [a]) -> [b] -> [c] -> [(b,c)] 
gzip _ f bs cs = zip (f bs) (f cs) 

> gzip (Proxy :: _ Enum) [0, 1] "ab" 
[(1,'b'),(2,'c')] 

GHC considera parametri dei vincoli che si verificano solo in vincoli ambigui, quindi abbiamo bisogno di registrare in modo esplicito in un proxy.

+0

Uso interessante di 'PartialTypeSignatures'. – kosmikus

+0

@kosmikus Sono stanco di doppio proxy: P –

+0

Sì. Certamente lo terrò a mente. Anche se ancora non mi piace molto che 'PartialTypeSignatures' siano un po 'fori di livello di tipo conflittuale con inferenza di codice a livello di codice. Vorrei che fossero sintatticamente diversi come in Agda. – kosmikus