2013-06-14 18 views
24

Ho alcune difficoltà a capire quando uso e quando non uso typeclass nel mio codice. Intendo lo creare il mio e non lo utilizzare i typeclass già definiti, naturalmente. L'esempio (molto stupido esempio), devo fare:Devo usare typeclass o no?

data Cars = Brakes | Wheels | Engine 
data Computers = Processor | RAM | HardDrive 

class Repairable a where 
    is_reparaible :: a -> Bool 

instance Repairable Cars where 
    is_repairable (Brakes) = True 
    is_repairable (Wheels) = False 
    is_repairable (Engine) = False 

instance Repairable Computers where 
    is_repairable (Processor) = False 
    is_repairable (RAM)  = False 
    is_repairable (HardDrive) = True 

checkState :: (Reparaible a) => a -> ... 
checkState a = ... 

(Ovviamente, questo è un stupido, esempio incompleto).

Ma questo è molto per un piccolo uso, no? Perché non dovrei fare qualcosa di semplice e solo definire le funzioni senza definire nuovi tipi di dati e tipografie (con le loro istanze).

Questo esempio è troppo semplice, ma in realtà vedo spesso qualcosa di simile (nuovi tipi di dati + typeclass + istanze) quando sfoglio il codice Haskell su github anziché solo le funzioni di definizione.

Quindi, quando dovrei creare nuovi tipi di dati, typeclass ecc. E quando devo usare le funzioni?

Grazie.

risposta

40

Perché non dovrei fare qualcosa di semplice e solo la definizione delle funzioni senza definire nuovi tipi di dati e typeclasses (con i loro casi).

Perché davvero? Si potrebbe semplicemente definire:

checkState :: (a -> Bool) -> (a -> b) -> (a -> b) -> a -> b 
checkState is_repairable repairs destroy a 
    = if (is_repairable a) then repairs a else destroy a 

Le persone usano improprio classi di tipi tutto il tempo. Ciò non significa che sia idiomatico.

Per rispondere alla tua domanda più generale, qui ci sono alcune regole pratiche per quando usare le classi di tipo e quando non usarli:

classi uso tipo se:

  • C'è un solo comportamento corretto per determinato tipo

    equazioni
  • La classe tipo ha associato (vale a dire "leggi") che tutte le istanze devono soddisfare

Non utilizzare classi di tipo se:

  • si sta cercando di appena le cose dello spazio dei nomi. Ecco a cosa servono moduli e spazi dei nomi.

  • Una persona che utilizza la classe tipo non può ragionare su come si comporterà senza guardare il codice sorgente delle istanze

  • si scopre che le estensioni si deve attivare sono sempre fuori controllo

+0

Sì! Questa è una risposta molto completa, grazie mille! Il tuo "non usare classi di tipi se ..." sarà particolarmente utile per scegliere il buon modo di fare qualcosa. – vildric

+1

Vorrei aggiungere a questo "non usare i typeclass solo per un metodo", sebbene questa non sia una regola assolutamente rigida, più simile a un suggerimento generico. – MathematicalOrchid

+2

@MathematicalOrchid Questa è una parte della regola "leggi di necessità" poiché raramente si hanno leggi per una classe di tipi con un solo metodo (ad eccezione di qualcosa come "SemiGroup", dove si ha la legge di associatività) –

5

Spesso è possibile utilizzare un tipo di dati anziché una classe di tipo, ad es.

data Repairable a = Repairable 
    { getRepairable :: a 
    , isRepairable :: Bool 
    , canBeRepairedWith :: [Tool] -> Bool -- just to give an example of a function 
    } 

Naturalmente è necessario passare questo valore in modo esplicito, ma questo può essere una buona cosa se si dispone di più scelte (per esempio pensare Sum e Product come possibili Monoid s per i numeri). Tranne che hai più o meno la stessa espressività di una classe tipo.

+2

Non sono il downvoter, ma. . . questa risposta in realtà non risponde alla domanda IMHO. L'OP chiede quando devono essere usati i tipi di carta. Questa risposta menziona un'alternativa ai typeclass, ma non dice quando usare quell'alternativa. (Se mai, sembra * presupporre * una comprensione di quando i typeclass hanno un senso e offre un'alternativa che ha senso in molti degli stessi casi). – ruakh