2017-02-09 27 views
6

Tôi muốn tạo một biểu thức sao cho tôi gặp lỗi compiletime hoặc URI.URI đã kiểm tra thời gian biên dịch

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

nên biên dịch, nhưng

[uri|foo:/bar:\|] 

không nên.

Tôi đã gặp QuasiQuotes, điều này rõ ràng là do loại vấn đề này. Tuy nhiên, tôi dường như không thể tạo số Q Exp từ số URI được phân tích cú pháp.

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 |] 
        } 

Không biên dịch, vì nó muốn ví dụ Lift cho URI. Tuy nhiên, tôi không chắc chắn làm thế nào để tạo ra một, do tính chất GADT.

deriving instance Lift (URIRef a) 

Khiếu nại về số Lift ByteString, nhưng tôi không có ý định viết một. Một cách khác sẽ là Data URI, nhưng điều đó không thành công với

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) 

Tôi muốn sử dụng Generics, nhưng tôi không chắc chắn làm thế nào để sử dụng chúng với các API QQ.

Trả lời

10

Bạn sắp hoàn tất - ví dụ Lift Bytestring bạn đang tìm được cung cấp trong gói th-lift-instances.

import Instances.TH.Lift 

Tất nhiên, bạn cũng có thể chỉ sao chép trường hợp có liên quan thay vì phát sinh phụ thuộc.

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

Sau đó, với DeriveLift, StandaloneDeriving, GADTs, và TemplateHaskell bật, bạn có thể tạo trẻ mồ côi Lift trường hợp cho tất cả các loại URIRef phụ thuộc (transitively) trên.

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 

Với phần bổ sung này, mã của bạn hiện đang biên dịch. Tại GHCi tôi nhận được sự tương tác sau đây, xác nhận mọi thứ hoạt động.

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> 

EDIT

Chỉ cần nhận thấy tôi không bao giờ trả lời phần cuối của câu hỏi của bạn.

Tôi muốn sử dụng Generics, nhưng tôi không biết cách sử dụng chúng với các API QQ.

Điều đó sẽ không thể - lập trình chung sẽ không cho phép bạn thực thi mã xác thực tùy ý tại thời gian biên dịch. Bạn thực sự cần TemplateHaskell cho việc này. Tốt nhất bạn có thể sử dụng chúng bên trong mã TemplateHaskell được tạo ra, nhưng điều đó sẽ không cần thiết (không có gì chung để làm ở đó).

Các vấn đề liên quan