2016-08-04 14 views
2

Ich fummle mit Haskell herum, aber ich kämpfe mit einem Problem. Der folgende Code:Der erwartete Typ 'r' konnte nicht mit dem tatsächlichen Typ 'Pferd' verglichen werden.

class HasHorses e where yieldInHorses :: (InHorses r) => e -> r 
class InHorses e 

data Horse = Horse String 
data Stable = Stable String 

instance InHorses Horse 
instance HasHorses Stable where yieldInHorses e = (Horse "Buttercup") 

gibt mir die folgende Fehlermeldung:

source_file.hs:10:52: error: 
    • Couldn't match expected type ‘r’ with actual type ‘Horse’ 
     ‘r’ is a rigid type variable bound by 
     the type signature for: 
      yieldInHorses :: forall r. InHorses r => Stable -> r 
     at source_file.hs:10:33 
    • In the expression: (Horse "Buttercup") 
     In an equation for ‘yieldInHorses’: 
      yieldInHorses e = (Horse "Buttercup") 
     In the instance declaration for ‘HasHorses Stable’ 
    • Relevant bindings include 
     yieldInHorses :: Stable -> r 
      (bound at source_file.hs:10:33) 

Wo Linie 10 zur Linie bezieht, in dem ich instance HasHorses Stable where ....

Vielleicht habe ich etwas übersehen, aber ich kann nicht sehen, warum dies ein Fehler sein sollte. Das tatsächliche Type Horse erfüllt die Bedingung von r, nämlich dass es InHorses sein sollte.

Die Idee der 'yieldInHorses' Methode ist, dass für etwas, das HasHorses, Aufruf dieser Methode darauf sollte mir sagen, die Pferde (nun, Pferd, um zu beginnen - Dies wird Listen bald umfassen), dass es hat.

Habe ich einen einfachen Fehler gemacht oder etwas grundlegenderes falsch verstanden?

+2

Die Art der 'yieldInHorses' ist eigentlich' forall r. InHorse r => e -> r', was vor allem bedeutet, dass der * Caller entscheiden muss, was "r" sein soll. Was du suchst, ist etwas wie 'exists' anstatt' forall', was in Haskell nicht direkt verfügbar ist, obwohl [es ist möglich, etwas wie sie mit anderen Konstrukten zu kodieren] (https://wiki.haskell.org/ Existential_type). Dies ist jedoch ein so konstruiertes Beispiel, dass es schwierig ist, konkrete Ratschläge zu geben - warum würden Sie jemals eine Typenklasse wie "InHorses" brauchen? –

+1

Anstelle von 'InHorses r => e -> r 'benutzen Sie einfach' e -> Horses', wobei 'Horses 'ein Typ ist, der alle gewünschten Informationen korrekt aufnimmt, wahrscheinlich' [Horse] '. – user2407038

+1

Ich frage mich, warum Sie Typklassen verwenden. Brauchst du sie wirklich? Wenn eine Typklasse nur genau eine Instanz hat, sollten Sie sie wahrscheinlich verwerfen und direkt den einfachen Typ verwenden. – chi

Antwort

3

Ich denke, Sie suchen nach zugeordneten Typen.

{-# LANGUAGE TypeFamilies #-} 
{-# LANGUAGE FlexibleContexts #-} 

class HasHorses e where 
    type R e :: * 
    yieldInHorses :: (InHorses (R e)) => e -> R e 

class InHorses e 

data Horse = Horse String 
data Stable = Stable String 

instance InHorses Horse 

instance HasHorses Stable where 
    type R Stable = Horse 
    yieldInHorses e = (Horse "Buttercup") 

test = yieldInHorses (Stable "asd") 

Hier, wenn wir definieren die HasHorses Instanz wir auch definiert, was das zugehörige Pferd Typ ist.

Wie @Alexis King erwähnt, erlaubt Ihre Unterschrift für yieldInHorses der Anrufer zu bestimmen, was der Rückgabetyp ist. Zum Beispiel, sie könnte fragen:

yieldInHorses (Stable "asd") :: Int 

Aber es scheint, dass Sie eine bestimmte Art auf der Instanz auf Basis zurückkehren möchten Sie definieren.

aktualisieren

Hier ist anthere Weise die Signatur für yieldInHorses zu schreiben, die für FlexibleContexts die Notwendigkeit beseitigt und arbeitet mit GHC 8:

yieldInHorses :: (InHorses r, r ~ R e) => e -> r 
+0

Ich denke, Sie müssen entweder '{- # LANGUAGE ConstrainedClassMethods # -}' hinzufügen oder '(InHorses (R e)) => HasHorses e' in eine Klassendefinition einfügen, um diese in GHC 8.0.1 zu kompilieren. –

+0

Ok Danke - ich habe es nur mit 7.10.x getestet. – ErikR

+0

Die Antwort wurde mit einer alternativen Signatur für yieldInHorses aktualisiert. – ErikR

0

Wie oben in einem Kommentar erwähnt, die Art (InHorses r) => e -> r von yieldInHorses bedeutet, dass, wenn ein Wert vom Typ e angegeben wird, ein Wert von Typ r t zurückgegeben werden kann Hut erfüllt die Bedingung InHorses. Die Implementierung gibt jedoch immer einen spezifischen r, Horse zurück.

Es ist schwer zu wissen, was am besten für Sie funktioniert, da das Beispiel unvollständig zu sein scheint (der Stable Wert wird in der Implementierung von yieldInHorses ignoriert). Wenn ein Stable kann nur produzieren Horse s, die funktionale Abhängigkeit oder Typ Familie Ansätze macht Sinn. Wenn jedoch ein Stable einen Zustand beibehält, der dann dazu verwendet werden kann, irgendeinen InHorses r => r zu erzeugen, könnte vielleicht ein Konstruktor-Ansatz nützlich sein.Wenn zum Beispiel alle InHorses sind in der Lage von einem String gebaut werden:

class InHorses e where makeHorse :: String -> e 
instance InHorses Horse where makeHorse s = Horse s 

instance HasHorses Stable where yieldInHorses e = makeHorse "Buttercup" 
Verwandte Themen