2017-02-09 102 views
6

我想製作一個表達式,以便我有編譯時錯誤或URI編譯時檢查的URI

[uri|http://stackoverflow.com|] 

應該編譯,但

[uri|foo:/bar:\|] 

不應該。

我碰到過QuasiQuotes,這顯然是針對這類問題。但是,我似乎無法從解析的URI創建Q Exp

import Language.Haskell.TH.Quote 
import Language.Haskell.TH.Syntax 
import Language.Haskell.TH 
import URI.ByteString 
import Data.ByteString.Char8 


uri = QuasiQuoter { quoteExp = \s -> 
         let 
         uri = either (\err -> error $ show err) id (parseURI laxURIParserOptions (pack s)) 
         in 
         [| uri |] 
        } 

不能編譯,因爲它要一個Lift實例URI。但是,由於GADT的特性,我不確定如何創建一個。

deriving instance Lift (URIRef a) 

抱怨沒有Lift ByteString,但我不知道寫一個。另一種方式是在Data URI,但失敗

85 1 error   • Couldn't match type ‘a’ with ‘Absolute’ 
    ‘a’ is a rigid type variable bound by 
    the instance declaration at uri-bytestring/src/URI/ByteString/Types.hs:85:1 
    Expected type: c (URIRef a) 
    Actual type: c (URIRef Absolute) 
• In the expression: k (k (k (k (k (z URI))))) 
    In a case alternative: 
     ghc-prim-0.5.0.0:GHC.Types.I# 1# -> k (k (k (k (k (z URI))))) 
    In the expression: 
    case constrIndex c of { 
     ghc-prim-0.5.0.0:GHC.Types.I# 1# -> k (k (k (k (k (z URI))))) 
     _ -> k (k (k (k (z RelativeRef)))) } 
    When typechecking the code for ‘gunfold’ 
    in a derived instance for ‘Data (URIRef a)’: 
    To see the code I am typechecking, use -ddump-deriv 
• Relevant bindings include 
    gunfold :: (forall b r. Data b => c (b -> r) -> c r) 
       -> (forall r. r -> c r) -> Constr -> c (URIRef a) 
     (bound at uri-bytestring/src/URI/ByteString/Types.hs:85:1) (haskell-stack-ghc) 

我寧願使用Generics,但我不知道如何將它們與QQ的API使用。

回答

10

您幾乎在那裏 - 您正在尋找的Lift Bytestring實例在th-lift-instances包中提供。

import Instances.TH.Lift 

當然,你也可以複製相關的實例,而不是產生依賴關係。

-- ByteString 
instance Lift ByteString where 
    lift b = [| pack $(lift $ unpack b) |] 

然後,DeriveLiftStandaloneDerivingGADTsTemplateHaskell開啓,可以爲所有類型的URIRef取決於(及物動詞)上創建孤兒Lift實例。

deriving instance Lift (URIRef a) 
deriving instance Lift Authority 
deriving instance Lift UserInfo 
deriving instance Lift Query 
deriving instance Lift Host 
deriving instance Lift Port 
deriving instance Lift Scheme 

此外,您的代碼現在編譯。在GHCi我得到以下互動,確認一切正常。

ghci> :set -XQuasiQuotes 
ghci> [uri|http://stackoverflow.com|] 
URI {uriScheme = Scheme {schemeBS = "http"}, uriAuthority = Just (Authority {authorityUserInfo = Nothing, authorityHost = Host {hostBS = "stackoverflow.com"}, authorityPort = Nothing}), uriPath = "", uriQuery = Query {queryPairs = []}, uriFragment = Nothing} 
ghci> [uri|foo:/bar:\|] 

<interactive>:3:1: error: 
    • Exception when trying to run compile-time code: 
     MalformedPath 
CallStack (from HasCallStack): 
    error, called at uri.hs:25:47 in main:Main 
     Code: quoteExp uri "foo:/bar:\\" 
    • In the quasi-quotation: [uri|foo:/bar:\|] 
ghci> 

編輯

只注意到我根本沒有回答你的問題的最後一部分。

我更喜歡使用泛型,但我不確定如何將它們與QQ API一起使用。

這是不可能的 - 泛型編程不會讓你在編譯時執行任意的驗證代碼。你真的需要TemplateHaskell。最好你可以在生成的TemplateHaskell代碼中使用它們,但這不是必需的(這裏沒有什麼通用的)。