【问题标题】:Compile-time checked URIs编译时检查的 URI
【发布时间】:2017-06-26 20:19:30
【问题描述】:

我想创建一个表达式,使我有编译时错误或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 |]
                  }

不编译,因为它需要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 一起使用。

【问题讨论】:

    标签: haskell types ghc template-haskell


    【解决方案1】:

    您就快到了 - 您正在寻找的 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 代码中使用它们,但那是不必要的(那里没有通用的操作)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-09-03
      • 1970-01-01
      • 2013-08-20
      • 2020-01-07
      • 2011-02-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多