编译时检查的 URI

Compile-time checked URIs

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

[uri|http://whosebug.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 |]
                  }

不编译,因为它需要 URILift 实例。但是,由于 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 一起使用。

您就快完成了 - th-lift-instances 包中提供了您正在寻找的 Lift Bytestring 实例。

import Instances.TH.Lift

当然,你也可以只复制相关实例而不产生依赖。

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

然后,在 DeriveLiftStandaloneDerivingGADTsTemplateHaskell 开启的情况下,您可以为所有类型创建孤立的 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

有了这个添加,您的代码现在可以编译了。在 GHCi,我得到以下交互,确认一切正常。

ghci> :set -XQuasiQuotes
ghci> [uri|http://whosebug.com|]
URI {uriScheme = Scheme {schemeBS = "http"}, uriAuthority = Just (Authority {authorityUserInfo = Nothing, authorityHost = Host {hostBS = "whosebug.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>

编辑

刚注意到我没有回答你问题的最后一部分。

I'd prefer to use Generics, but I'm not sure how to use them with the QQ APIs.

那是不可能的 - 泛型编程不允许您在编译时执行任意验证代码。为此,您确实需要 TemplateHaskell。充其量你可以在生成的 TemplateHaskell 代码中使用它们,但这是不必要的(那里没有通用的东西)。