Come consigliato da @ carsten-könig, una soluzione sarebbe quella di creare un wrapper newtype
per Char
. Questa non è una soluzione alternativa, ma un modo corretto e davvero efficace per sfuggire a un'intera classe di problemi correlati alle istanze orfane (istanze per le classi di caratteri definite in un altro modulo), maggiori informazioni su tali problemi here.
Inoltre, questo approccio è ampiamente utilizzato quando ci sono diverse istanze con comportamenti diversi.
Ad esempio, si consideri il Monoid
typeclass che è definito in Data.Monoid
come:
class Monoid a where
mempty :: a --^Identity of 'mappend'
mappend :: a -> a -> a --^An associative operation
Come si può già sapere Monoide è un tipo di valori che possono essere aggiunto tra loro (mediante mappend
) e per i quali esiste un valore di "identità" mempty
che soddisfa una regola mappend mempty a == a
(aggiunta di un'identità al valore a
in a
). C'è un caso evidente di Monoide per gli elenchi:
class Monoid [a] where
mempty = []
mappend = (++)
E 'anche facile da definire Int
s. Infatti gli interi con l'operazione di addizione formano un monoide corretto.
class Monoid Int where
mempty = 0
mappend = (+)
Ma è l'unico monoid possibile per numeri interi? Naturalmente non è, la moltiplicazione su interi sarebbe formare un altro adeguato monoide:
class Monoid Int where
mempty = 1
mappend = (*)
Entrambe le istanze sono corrette, ma ora abbiamo un problema: se si desidera cercare di valutare 1 `mappend` 2
, non c'è modo di capire quale istanza deve essere utilizzata.
Ecco perché Data.Monoid
avvolge le istanze per i numeri in wrapper newtype, ovvero Sum
e Product
.
Andando oltre, la sua dichiarazione
instance Arbitrary Char where
arbitrary = choose ('0', '9')
potrebbe essere molto confusa. Dice "Sono un personaggio arbitrario" ma produce solo caratteri numerici. A mio parere, sarebbe molto meglio:
newtype DigitChar = DigitChar Char deriving (Eq, Show)
instance Arbitrary DigitChar where
arbitrary = fmap DigitChar (choose ('0', '9'))
Un pezzo di torta. Puoi andare oltre e nascondere un costruttore DigitChar
, fornendo "costruttore intelligente" digitChar
, che non consentirebbe di creare un DigitChar
che in realtà non è una cifra.
quanto della tua domanda "Sai perché non è questo l'approccio del tutorial ha preso?", Penso che il motivo è semplice, il tutorial sembra essere scritto nel 2006, e in those days quickcheck semplicemente non ha definito un Arbitrary
istanza per Char
. Quindi il codice nel tutorial era perfettamente valido nel passato.
vorrei solo avvolgere 'char' in un' newtype' come 'Newtype cifra = Cifra char' e fare questo in un caso – Carsten
@ CarstenKönig, molto buona soluzione. Inoltre mi impedisce di usare caratteri generali in cui dovrei usare solo cifre. Sai perché non è questo l'approccio che ha preso il tutorial? – Turion