Als @ carsten-könig beraten, wäre eine Lösung, eine newtype
Wrapper für Char
zu machen. Das ist keine Problemumgehung, aber eine richtige und wirklich nette Möglichkeit, eine ganze Klasse von Problemen zu umgehen, die verwaisten Instanzen (Instanzen für Typklassen, die in einem anderen Modul definiert sind) zugeordnet sind, lesen Sie mehr über solche Probleme here.
Darüber hinaus ist dieser Ansatz weit verbreitet, wenn es mehrere mögliche Instanzen mit unterschiedlichem Verhalten gibt.
Betrachten wir zum Beispiel die Monoid
typeclass, die in Data.Monoid
wie folgt definiert ist:
class Monoid a where
mempty :: a --^Identity of 'mappend'
mappend :: a -> a -> a --^An associative operation
Wie Sie vielleicht schon wissen, Monoid eine Art von Werten, die aneinander gehängt werden kann (unter Verwendung von mappend
) und für die Es existiert ein "Identitäts" -Wert mempty
, der eine Regel mappend mempty a == a
erfüllt (Anfügen einer Identität an Wert a
ergibt a
). Es ist offensichtlich, dass die Instanz von Monoid für Listen:
class Monoid [a] where
mempty = []
mappend = (++)
Es ist auch einfach Int
s zu definieren. Tatsächlich bilden ganze Zahlen mit Additionsoperation ein korrektes Monoid.
class Monoid Int where
mempty = 0
mappend = (+)
Aber ist es das einzige mögliche Monoid für ganze Zahlen? Natürlich ist es nicht, Multiplikation mit ganzen Zahlen eine andere geeignete Monoid bilden würde:
class Monoid Int where
mempty = 1
mappend = (*)
Beide Instanzen korrekt sind, aber jetzt haben wir ein Problem: Wenn Sie 1 `mappend` 2
zu bewerten versuchen würde, auf keinen Fall ist es herauszufinden, welche Instanz muss verwendet werden.
Deshalb wickelt Data.Monoid
die Instanzen für Zahlen in newtype Wrapper, nämlich Sum
und Product
.
Weiter zu gehen, Ihre Aussage
instance Arbitrary Char where
arbitrary = choose ('0', '9')
könnte sehr verwirrend sein. Es heißt "Ich bin ein beliebiger Charakter", produziert aber nur Ziffern. Meiner Meinung nach wäre das viel besser:
newtype DigitChar = DigitChar Char deriving (Eq, Show)
instance Arbitrary DigitChar where
arbitrary = fmap DigitChar (choose ('0', '9'))
Ein Stück Kuchen. Sie können weiter gehen und einen DigitChar
Konstruktor verbergen, der digitChar
'intelligenten Konstruktor' zur Verfügung stellt, der nicht erlauben würde, eine DigitChar
zu schaffen, die nicht wirklich eine Ziffer ist.
Als Ihre Frage "Weißt du, warum das ist nicht die Vorgehensweise, die das Tutorial dauerte?", Ich denke, der Grund ist einfach, das Tutorial scheint im Jahr 2006 geschrieben zu sein, und in those days quickcheck einfach nicht definiert Arbitrary
Instanz für Char
. So war der Code im Tutorial in den vergangenen Tagen perfekt.
Ich würde nur wickeln 'Char' in eine' newtype' wie 'newtype Digit = Digit Char' und diese – Carsten
@ CarstenKönig in eine Instanz machen, sehr gute Problemumgehung. Verhindert auch, dass ich allgemeine Zeichen verwende, wo ich nur Ziffern verwenden soll. Weißt du, warum das nicht der Ansatz war, den das Tutorial genommen hat? – Turion