2015-08-28 8 views
5

Ich versuche, eine Typ-Signatur für eine Funktion in Vorlage Haskell zu erstellen. Gibt es eine einfache Möglichkeit, dies zu tun?Spleißart Unterschrift in Vorlage Haskell

Ich habe einige Workarounds, um es in der Zwischenzeit zu lösen, aber es sollte einfacher sein, oder?

-- TH.hs 
module Lib.TH (mkFunction) where 

import Language.Haskell.TH 

mkFunction n = do 
    let name = mkName n 
    [d| 
    $(...) :: Integer -> Integer 
    $(varP name) = \x -> x + 2|] 

-- Other.hs 
import TH 

mkFunction "test" 

Was soll ich in der $(...) oben schreiben? Alles, was ich habe versucht, Ergebnisse in

Invalid type signature: ... :: Integer -> Integer 
Should be of form <variable> :: <type> 
+1

Dies kann nicht befriedigend sein, aber Sie können '$ (varP name) = (\ x -> x + 2) :: Ganze Zahl -> Ganze Zahl – luqui

+0

Ich habe versucht, aber leider brauche ich eine Einschränkung' (T a) => a -> a', und es funktioniert nicht, wenn ich das einschließe. – Hashmush

Antwort

3

ich keinen TH-Experten bin, aber ich fand einen Weg durch um in der Dokumentation zu graben und nach den Typfehlern.

import Language.Haskell.TH 
import Control.Applicative ((<$>)) 

mkFunction n = do 
    let name = mkName n 
    [d| 
     $(return . SigD name <$> [t| Integer -> Integer |]) 
     $(varP name) = \x -> x + 2 |] 

Ich weiß nicht, ob es einen saubereren Weg gibt.

HINWEIS dies funktioniert am 7.8.3, aber nicht am 7.10.2. :-(

+1

Mit 7.10.2 wird dieser Kompilierfehler ausgelöst: 'Spleiße innerhalb von Deklarationsklammern, die (noch) nicht von Template Haskell bearbeitet wurden? – Sibi

+0

@Sibi, das ist schade. Worksforme bei 7.8.3, vielleicht eine Regression. Wie auch immer, ich habe meine Antwort mit einer alternativen Formulierung bearbeitet, die das Problem lösen könnte. Kannst du es versuchen? – luqui

+0

Es wirft den gleichen Fehler mit dem neuen Code. – Sibi

1

ich nicht denken (Vermutung) gibt es einen Weg gibt, den Namen in einem Template Haskell Typ Signatur (für GHC ≤ 7,10). Um noch die quasi-Angabe verwenden, um Spleiß, können Sie versuchen, verarbeiten die AST danach den Namen gegebenenfalls (und den Rest unberührt) einzustellen.

setName :: Name -> DecsQ -> DecsQ 
setName = fmap . map . sn 
    where sn n (SigD _ t)   = SigD n t 
     sn n (ValD (VarP _) x y) = ValD (VarP n) x y 
     sn _ d     = d 

mkFunction n = setName (mkName n) 
    [d| 
    n :: Integer -> Integer 
    n = \x -> x + 2 |] 

Dieses auf GHC getestet wurde 7,10 und wahrscheinlich arbeiten auf vergangene und zukünftige Versionen von GHC mit geringfügigen Modifikationen

Inwieweit dies weniger ausführlich ist als das direkte Schreiben der AST, ist fraglich, es hängt von der Häufigkeit ab, die Sie haben Schreibe solche Deklarationen und die Komplexität des zitierten Codes.

Die obige setName-Funktion wird offensichtlich brechen, wenn Sie es für eine Deklaration mit mehreren Funktionen (oder rekursiven Funktionen) verwenden. Um das anzugehen, könnten Sie eine reName Funktion in demselben Sinne schreiben.