编译时检查的URI

Rea*_*onk 6 haskell types ghc template-haskell

我想创建一个表达式,以便我有一个编译时错误或URI.

[uri|http://stackoverflow.com|]
Run Code Online (Sandbox Code Playgroud)

应该编译,但是

[uri|foo:/bar:\|]
Run Code Online (Sandbox Code Playgroud)

不应该.

我遇到过QuasiQuotes,显然是出于这种问题.但是,我似乎无法Q Exp从解析中创建URI.

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 |]
                  }
Run Code Online (Sandbox Code Playgroud)

不编译,因为它想要一个Lift实例URI.但是,由于GADT的性质,我不确定如何创建一个.

deriving instance Lift (URIRef a)
Run Code Online (Sandbox Code Playgroud)

抱怨不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)
Run Code Online (Sandbox Code Playgroud)

我更喜欢使用Generics,但我不确定如何将它们与QQ API一起使用.

Ale*_*lec 10

你几乎就在那里 - Lift Bytestring你正在寻找的实例是在th-lift-instances包中提供的.

import Instances.TH.Lift
Run Code Online (Sandbox Code Playgroud)

当然,您也可以只复制相关实例而不是产生依赖关系.

-- ByteString
instance Lift ByteString where
  lift b = [| pack $(lift $ unpack b) |]
Run Code Online (Sandbox Code Playgroud)

然后,DeriveLift,StandaloneDeriving,GADTs,和TemplateHaskell开启,您可以创建孤儿Lift为所有类型的实例URIRef依赖(及物动词).

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
Run Code Online (Sandbox Code Playgroud)

通过此添加,您的代码现在可以编译.在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>
Run Code Online (Sandbox Code Playgroud)

编辑

刚注意到我从未回答你问题的最后部分.

我更喜欢使用Generics,但我不确定如何将它们与QQ API一起使用.

这是不可能的 - 通用编程不会让你在编译时执行任意验证代码.你真的需要TemplateHaskell这个.充其量你可以在TemplateHaskell生成的代码中使用它们,但这是不必要的(那里没有通用的).