2016-05-23 13 views

Antwort

7

Nicht möglich. Well-Kinded-Familienanwendungen lösen Fehler niemals selbst aus. Stattdessen erhalten wir nur Typfehler, wenn wir versuchen, unreduzierte Familienausdrücke für etwas zu verwenden.

Wir können benutzerdefinierte Typen halten Fehlermeldungen verwenden, um Fehler etwas klarer zu machen:

import GHC.TypeLits 

data Error (msg :: Symbol) -- a type-level error message 

type family Testf a where 
    Testf Char = IO() 
    Testf String = IO() 
    Testf x  = Error "No match for Testf" 

Nun wirft GHC einen Fehler und folglich druckt unsere Nachricht, wenn wir versuchen, Error msg zu verwenden, um einen nicht zu geben -undefinierter Wert

Von GHC 8.0 können wir TypeError verwenden unsere Botschaft in eine schönere Art und Weise zu drucken:

{-# language DataKinds #-} 
import GHC.TypeLits 

type family Testf a where 
    Testf Char = IO() 
    Testf String = IO() 
    Testf x  = TypeError (Text "No matching case for Testf") 

Dies würde drucken:

Notes.hs:18:5: error: … 
    • No matching case for Testf 
    • In the expression: ... 

Dies ist jedoch nach wie vor nur führt den Fehler zur Anwendung:

type T = Testf Int -- this typechecks 

x :: T 
x =() -- we throw error only here 
+3

GHC 8.0 bietet so etwas wie dies in 'TypeLits', mit spezieller Typ-Checker Unterstützung der Fehler gut aussehen zu lassen. – dfeuer

+2

Guter Fund, zusätzlicher Hinweis darauf. –

4

Das ist unmöglich in GHCs vor 8.0, aber the (as of this writing) just-released GHC 8.0.1 add s Unterstützung für custom type errors.

Die Idee ist, dass, genau wie die Funktion error :: String -> a auf dem Begriff Ebene jede Art mit einem Fehler bewohnt, die wir jetzt haben, in GHC.TypeLits, die Art Familie

type family TypeError (msg :: ErrorMessage) :: k 

, dass jeder Typen mit einem bewohnt Typ Fehler. Der ErrorMessage Typ ist sehr einfach:

data ErrorMessage = Text  Symbol 
        | ShowType t 
        | ErrorMessage :<>: ErrorMessage 
        | ErrorMessage :$$: ErrorMessage 

Der Konstruktor (:<>:) verkettet zwei Fehlermeldungen horizontal; Der (:$$:) Konstruktor verkettet sie vertikal. Die anderen beiden Konstrukteure tun, was sie sagen.

In Ihrem Beispiel können Sie also den letzten Fall mit einem ausfüllen; zum Beispiel

type family Testf a where 
    Testf Char = IO() 
    Testf String = IO() 
    Testf a  = TypeError ( Text "‘Testf’ didn't match" 
          :$$: Text "when applied to the type ‘" 
           :<>: ShowType a :<>: Text "’") 

Dann versuchen pure() bei Typ verwenden Testf Int wird

....hs:19:12: error: … 
    • ‘Testf’ didn't match 
     when applied to the type ‘Int’ 
    • In the expression: pure() 
     In an equation for ‘testfInt’: testfInt = pure() 
Compilation failed. 

Hinweis mit dem Fehler fehlschlagen, dass während

definiert
testfInt :: Testf Int 
testfInt = pure() 

korrekt brach, definiert

testfInt :: Testf Int 
testfInt = undefined 

(oder ähnlich mit testfInt = testfInt) hat gut funktioniert.


Hier ist eine vollständige Beispiel Quelldatei:

{-# LANGUAGE UndecidableInstances, TypeFamilies, DataKinds, TypeOperators #-} 

import GHC.TypeLits 

type family Testf a where 
    Testf Char = IO() 
    Testf String = IO() 
    Testf a  = TypeError ( Text "‘Testf’ didn't match" 
          :$$: Text "when applied to the type ‘" 
           :<>: ShowType a :<>: Text "’") 

testfChar :: Testf Char 
testfChar = putStrLn "Testf Char" 

testfString :: Testf Char 
testfString = putStrLn "Testf String" 

-- Error here! 
testfInt :: Testf Int 
testfInt = putStrLn "Int"