2016-08-05 14 views
7

bemerkte ich, dass die Testsuite für Data.Set wirklich Arbitrary Set a für a ~ Int vernünftig definiert nur, aber die GHC speziellen ~ es verwendetWie kann ich eine Instanz für eine bestimmte Anwendung in Haskell 98 definieren?

instance Enum a => Arbitrary (Set a) 

Wie kann ich sicher ist machen zu vermeiden, nur die Arbitrary (Set Int) Instanz, ohne dass irgendwelche GHC Erweiterungen verwendet ? In GHC-only-Code, würde ich entweder FlexibleInstances oder GADTs und dann entweder

instance Arbitrary (Set Int) 

oder

instance a ~ Int => Arbitrary (Set a) 

Antwort

6

Dies ist möglich, eine Idee mit Ich denke, ich zum ersten Mal in einem Papier von Oleg Kiselyov angetroffen, und was Control.Lens.Equality zugrunde liegt.

import Data.Functor.Identity 

class IsInt a where 
    fromIntF :: f Int -> f a 

instance IsInt Int where 
    fromIntF fx = fx 

toIntF :: IsInt a => g a -> g Int 
toIntF = unf . fromIntF . F $ id 

newtype F g a b = F {unf :: g b -> a} 

fromInt :: IsInt a => Int -> a 
fromInt = runIdentity . fromIntF . Identity 

toInt :: IsInt a => a -> Int 
toInt = runIdentity . toIntF . Identity 

Jetzt kann ich

instance IsInt a => Arbitrary (Set a) 

verwenden und sicher sein, dass ich wirklich mit Int zu tun habe. Der Einfachheit halber kann ich die IsInt Klasse mit allen Klassen beschränken, ich brauche von denen Int ist ein Beispiel:

class (Show a, Read a, Integral a, Arbitrary a) => IsInt a where ... 
+3

Es kann erwähnenswert sein: dies gibt Ihnen die Garantie, die Sie außerhalb des Systems wollen, aber innen ist es nicht. Das heißt, GHC wird nicht aus "IsInt a" das "a ~ Int" ableiten. Dies kann gelegentlich lästige zusätzliche Typ Anmerkungen erfordern. Ich glaube nicht, dass rein innerhalb von H98 (oder H2010) geholfen werden kann. –

+0

@DanielWagner, ja, es ist eindeutig schlechter als der Ansatz von GHC. Wenn Gleichheitsbegrenzungen jemals standardisiert werden, werde ich sie definitiv bevorzugen. Aber "Container" versuchen im Allgemeinen, so "tragbar" wie möglich zu sein, also wenn ich ohne eine Verlängerung tun kann, was ich brauche, werde ich es tun. – dfeuer

+1

@DanielWagner, ich denke, es ist auch erwähnenswert, dass diese Implementierung mit GHC kompatibel ist, in dem Sie 'fromIntF' auf' Refl :: Int: ~: Int' anwenden können, um etwas vom Typ 'IsInt a => Int: ~ zu bekommen : a'. In der Tat funktioniert dies für jeden ähnlichen Gleichheitstyp, unabhängig davon, wie er in die Sprache passt. – dfeuer