2011-06-08 12 views
14

Sto cercando di codificare un elenco di elementi che hanno limitato i tipi di essere le istanze di una classe tipo:Elenco degli elementi di tipi sono limitati dal typeclass

{-# LANGUAGE RankNTypes, TypeSynonymInstances, LiberalTypeSynonyms #-} 
module Test where 

class Someable a where 
    some :: a -> String 

data Some = Some String 

type SomeGroup = forall a. Someable a => [a] 

instance Someable Some where 
    some (Some v) = v 

instance Someable SomeGroup where 
    some (x:xs) = (some x) ++ ", " ++ (some xs) 

main = do 
    putStrLn $ show.some [Some "A", [Some "B", Some "C"]] 

Ma compilazione fallisce con l'errore:

Test.hs:14:10: 
    Illegal polymorphic or qualified type: SomeGroup 
    In the instance declaration for `Someable SomeGroup' 

sembra che persino riuscito a definire istanza per tipo sinonimo ...

sono consapevole di heterogenous collections articolo wiki, ma vogliono sapere perché esattamente il mio approccio non funziona - è Mi sembra naturale definire il tipo limitando la raccolta solo per contenere elementi con tipi che sono un'istanza di una classe tipo.

+1

Quello che stai cercando di fare qui ha senso, ma non è ben supportato da Haskell, ed è di solito un sintomo di un design non idiomatico. Se sei curioso di sapere come farlo, @hammar ti dà un punto di partenza. Se incontri questa situazione in codice reale, probabilmente starai meglio a ripensare il tuo approccio. –

risposta

9

Se ho capito le cose correttamente, ci deve essere un tipo di dati che avvolge l'esistente in modo che ci sia un luogo in cui memorizzare type class dictionary insieme a ciascun elemento.

Aggiungendo qualche wrapper lo fa funzionare:

{-# LANGUAGE ExistentialQuantification, TypeSynonymInstances #-} 

module Test where 

class Someable a where 
    some :: a -> String 

data Some = Some String 

data SomeWrapper = forall a. Someable a => SomeWrapper a 

type SomeGroup = [SomeWrapper] 

instance Someable Some where 
    some (Some v) = v 

instance Someable SomeWrapper where 
    some (SomeWrapper v) = some v 

instance Someable SomeGroup where 
    some (x:xs) = (some x) ++ ", " ++ (some xs) 

main = do 
    putStrLn $ some [SomeWrapper (Some "A"), SomeWrapper [SomeWrapper (Some "B"), SomeWrapper (Some "C")]] 

Naturalmente, questo è un po 'brutto. Sfortunatamente non sono a conoscenza di un modo migliore.

+2

Nota che il tipo che hai definito è diverso dal tipo nella domanda: '∀ x. C x => [x] 'è distinto da' [∀ x. C x => x] '. –

+0

@camccann: sei sicuro di leggere correttamente il mio codice? 'SomeWrapper' è' forall x. C x => x', e io uso '[SomeWrapper]'. – hammar

+0

@hammar: Sì, e la domanda ha il contrario. Confronta le definizioni di 'SomeGroup'. –

3

Puoi anche cucinare qualcosa usando GADT. In alcuni casi potrebbe essere leggermente più breve e rende esplicito quali dizionari di tipo sono disponibili dopo la corrispondenza del modello.

Ecco una leggera variante del vostro esempio:

{-# LANGUAGE GADTs #-} 
class Someable a where 
    some :: a -> String 

instance Someable Int where 
    some n = show n 

data SomeString = SomeString String 
instance Someable SomeString where 
    some (SomeString s) = s 

data SomeGroup where 
    Nil :: SomeGroup 
    Cons :: Someable a => a -> SomeGroup -> SomeGroup 

instance Someable SomeGroup where 
    some Nil = "" 
    some (Cons x Nil) = some x 
    some (Cons x xs) = some x ++ ", " ++ some xs 

list = Cons (3::Int) (Cons (SomeString "abc") (Cons (42::Int) Nil)) 
main = print . some $ list 

Un paio di note minori:

  • Hai dimenticato il caso base per la ricorsione :)
  • putStrLn . show è lo stesso di print .
  • È necessario specificare esplicitamente il tipo di numeri come Int poiché i valori letterali interi vengono trattati in modo specifico, ovvero 42 viene convertito in fromInteger 42 di tipo Num a => a. Un po 'goffi quando si costruisce la lista direttamente, ma la maggior parte delle volte funziona in modo più fluido.
  • Puoi naturalmente definire il tuo zucchero sintattico per gli elementi Cons -ing in un elenco, ma la sintassi non sarà mai così bella come la sintassi integrata di Haskell!

E un punto importante è che si perde l'uso di tutte le funzioni di elenco standard. Di solito uso questo tipo di soluzione quando le mie esigenze di elaborazione delle liste sono estremamente limitate; d'altra parte, una piega è scritta abbastanza velocemente ... La vostra situazione varia, però, non so cosa vogliate usare questa lista per.

Problemi correlati