2016-06-01 6 views
1
import Data.Maybe 

f :: (Show t) => Maybe t -> IO() 
f Nothing = putStrLn "Nothing!" 
f (Just x) = putStrLn $ "The number is " ++ (show x) 

main = do 
    f Nothing 

Das gibt:Typ Fehler in Haskell, wenn Muster mit anzeigen Einschränkung passend gegen Vielleicht

foo.hs:7:3: 
    No instance for (Show a0) arising from a use of ‘f’ 
    The type variable ‘a0’ is ambiguous 
    Note: there are several potential instances: 
     instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’ 
     instance Show Ordering -- Defined in ‘GHC.Show’ 
     instance Show Integer -- Defined in ‘GHC.Show’ 
     ...plus 22 others 
    In a stmt of a 'do' block: f Nothing 
    In the expression: do { f Nothing } 
    In an equation for ‘main’: main = do { f Nothing } 

foo.hs:7:3: 
    No instance for (Show a0) arising from a use of ‘f’ 
    The type variable ‘a0’ is ambiguous 
    Note: there are several potential instances: 
     instance Show a => Show (Maybe a) -- Defined in ‘GHC.Show’ 
     instance Show Ordering -- Defined in ‘GHC.Show’ 
     instance Show Integer -- Defined in ‘GHC.Show’ 
     ...plus 22 others 
    In a stmt of a 'do' block: f Nothing 
    In the expression: do { f Nothing } 
    In an equation for ‘main’: main = do { f Nothing } 

Wie kann dieser Frieden von Code festgelegt werden? Der Punkt ist, ein showable sicher zu drucken. Ich dachte, dass (t) zeigen könnte => Vielleicht könnte t in sowohl Nothing und Just x destrukturiert werden.

+0

Um Ihnen einen Hinweis zu geben, warum dieser Fehler existiert: Vergleichen Sie 'show ([] :: [Char])' und 'show ([] :: [Int])'. Dies macht es sehr deutlich, warum z.B. 'show []' ist mehrdeutig: Soll es den vorherigen oder den letzten Wert ergeben? Selbst für "Container", die keine Werte enthalten, muss man den Typ der enthaltenen Elemente festlegen, bevor das Verhalten von "show" bestimmt wird. –

Antwort

2

Hier Nothing ist jeder möglichen Maybe a müssen Sie angeben, welche a Sie bedeuten - versuchen

main :: IO() 
main = f (Nothing :: Maybe Int) 
+0

Sollte ich nicht das Nichts als etwas ähnlich wie Maybe Show t eingeben? –

+0

Ich denke, du kannst etwas wie '(Nothing :: forall t.Zeigen Sie t => Vielleicht t) 'aber ich habe es nicht versucht und ich bin mir fast sicher es wird nicht; 'f' ist in seinem Argument polymorph, aber Sie brauchen einen konkreten Typ, an dem' f' arbeitet. – epsilonhalbe

+1

@RodrigoStv Nein, Funktionsargumente sind in Haskell niemals (!) Polymorph. (Obwohl GHC Erweiterungen hat, um diese Einschränkung zu lockern.) –

4

Angenommen, du bist der Compiler, und Sie versuchen, Ihr Programm eingeben überprüfen:

main = do 
f Nothing 

Sie wissen, dass:

  1. main muss den Typ IO() haben, da es sich um eine vom Compiler auferlegte Anforderung handelt;
  2. f müssen Typ (Show t) => Maybe t -> IO() haben, weil das der Typ Annotation ist.
  3. Nothing muss Typ Maybe a haben, wobei a = t (das gleiche t wie in der Verwendung von f);
  4. Daher muss a eine Show Instanz sein.

Aber das ist nicht genug, um das Programm zu kompilieren, Haskell muss monomorphize der Anruf an f in Ihrer main Aktion, dh herauszufinden, die konkrete Art von jeder Art Variable in dieser speziellen Verwendung von f aus . In diesem Fall liegt das daran, dass der Typ von main :: IO() monomorph ist (keine Typvariablen darin), sodass keiner der Ausdrücke, die in seiner Definition vorkommen, Typen mit uninstantiierten Typvariablen haben kann.

Also das ist, was dieser zweideutige Art Fehler bedeutet: Ihr Programm verfügt nicht über genügend Informationen für die Compiler des Elementtyp des Maybe Arguments auf Ihren Anruf zu f herauszufinden. Alles, was es herausfinden kann, ist, dass es eine Instanz sein muss, aber das ist nicht genug, um das Programm zu kompilieren.

Die Lösung besteht darin, eine Typ-Annotation irgendwo hinzuzufügen, die einen konkreten Typ dafür auswählt. Es gibt viele Möglichkeiten:

main = f (Nothing :: Maybe Int) 

main = f argument 
    where argument :: Maybe Integer 
     argument = Nothing 

main = (f :: Maybe String -> IO()) Nothing 

Der Grund gibt es nicht nur einen Weg gibt, ist, dass Typinferenz wie ein Detektiv ist-es funktioniert, indem viele verschiedene Anhaltspunkte Zusammenstellen und somit gibt es viele verschiedene Hinweise Sie können Gib es, damit es "den Fall lösen kann".

+0

'main' kann einen' IO' Typ haben, nicht nur 'IO()', obwohl das hier nicht so relevant ist. – dfeuer

+0

@dfeuer: Huh, das wusste ich nicht. Vielen Dank! –

+0

Ich denke, es ist im Allgemeinen am klarsten, 'IO()' für Programme zu verwenden, die ordnungsgemäß enden können, und 'IO a' (polymorph) für Programme zu verwenden, die nur mit einer nicht abgefangenen Ausnahme enden, aber das ist ein bisschen nit -wählerisch. – dfeuer