2016-12-07 3 views
1

ich durch „Haskell Programmierung von ersten Prinzipien“ werde und ich fand ich in der folgenden Art und Weise des Schreiben von Code über und über:Abstracting HSPEC testet

type IntToInt = Fun Int Int 
type TypeIdentity = ConcreteFunctorType Int -> Bool 
type TypeComposition = ConcreteFunctorType Int -> IntToInt -> IntToInt -> Bool 

checkSomething :: IO() 
checkSomething = hspec $ do 
     describe "Some functor" $ do 
      it "identity property" $ do 
       property $ (functorIdentity :: TypeIdentity) 
      it "composition property" $ do 
       property $ (functorComposition :: TypeComposition) 

ich schon versucht, diese zu abstrahieren, aber auf meiner Ebene Ich bin nicht in der Lage

checkFunctor :: (Functor f) => String -> f a -> IO() 
checkFunctor description f = hspec $ do 
     describe description $ do 
      it "identity property" $ do 
       property $ (functorIdentity :: f a -> TypeIdentity) 
      it "composition property" $ do 
       property $ (functorComposition :: f a -> TypeComposition) 

EDIT Abbildung einen Ausweg, um es

funktioniert

was würde ich wie dieser

war etwas erreichen gern zu: nach Sapan oia Antwort habe ich versucht, wie

folgt
type TypeIdentity = Bool 
type TypeComposition = Fun Int Int -> Fun Int Int -> Bool 


checkFunctor :: forall f a. (Functor f) => String -> f a -> IO() 
checkFunctor description f = hspec $ do 
    describe description $ do 
     it "identity property" $ do 
      property $ (functorIdentity :: f a -> TypeIdentity) 
     it "composition property" $ do 
      property $ (functorCompose' :: f a -> TypeComposition) 

aber ich bekomme die folgende Fehler

FunctorCheck.hs:22:25: error: 
• Couldn't match type ‘a’ with ‘Int’ 
    ‘a’ is a rigid type variable bound by 
    the type signature for: 
     checkFunctor :: forall (f :: * -> *) a. 
         Functor f => 
         String -> f a -> IO() 
    at FunctorCheck.hs:16:26 
    Expected type: f a -> TypeComposition 
    Actual type: f Int -> Fun Int Int -> Fun Int Int -> Bool 

Es wird dann ziemlich kompliziert für mich die Typen zu definieren, die beliebige Werte und Funktionen zu erzeugen.

Gibt es eine Möglichkeit ich die Art von checkFunctor auf einen bestimmten Typ wie die folgende binden kann?

checkFuntor :: checkFunctor :: forall f Int. (Functor f) => String -> f a -> IO() 

natürlich habe ich versucht, diese und es gibt mir einen Parser-Fehler, nehme ich es nur mich nicht mit ‚forall‘ richtig.

+1

Ja, ist es möglich. Sie können dies tun 'ScopedTypeVariables' verwenden, und es kann mit GHC 8 der' TypeApplications' zusammen mit 'AllowAmbiguousTypes' noch schöner gemacht werden. –

+0

'forall' wird verwendet, um einen neuen Typ * variable * einzuführen. * Beton * -Typen wie "Int" müssen nicht eingeführt werden, sondern nur anstelle einer * -Typ-Variable *. Ihr Beispiel wird dann: 'checkFunctor :: forall f. (Functor f) => Zeichenkette -> f Int -> IO() '. Im Körper von 'checkFunctor' müssen Sie entsprechend:' functorIdentity :: f Int -> TypeIdentity'. – sapanoia

Antwort

1

Da Sie keine Fehlermeldung hinzugefügt haben, gehe ich davon aus das Problem eine Art Fehler ist, wo (functorIdentity :: f a -> TypeIdentity) definiert ist. Das Problem ist, dass die f hier eingeführt ist neu und unterscheidet sich von der f in der Top-Level-Signatur. Um dies zu beheben, aktivieren Sie die folgende Erweiterung:

{-# LANGUAGE ScopedTypeVariables #-} 

Und die Unterschrift von checkFunctor ändern:

checkFunctor :: forall f a. (Functor f) => String -> f a -> IO() 

forall neu einführt Typ Variablen. Ohne ScopedTypeVariables und eine explizite forall ist es immer implizit vorhanden, und (functorIdentity :: f a -> TypeIdentity) wird (functorIdentity :: forall f a. f a -> TypeIdentity). Allerdings tun Sie nicht wollen ein Forall hier, weil Sie die Typvariablen f und a die gleichen wie die Top-Level sein wollen.

+0

Vielen Dank für Ihre Antwort! Ich habe die Frage bearbeitet, nachdem ich das versucht habe, allerdings ohne Erfolg –