Time-Validated URIs

I want to make such an expression to have a compilation error or URI .

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

should compile but

 [uri|foo:/bar:\|] 

should not.

I came across QuasiQuotes, which are apparently for this problem. However, I cannot create Q Exp from a URI parsing.

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

It does not compile because it needs a Lift instance for the URI . However, I'm not sure how to create it, due to the nature of GADT.

 deriving instance Lift (URIRef a) 

Complains about no Lift ByteString , but I have no idea to write it. Another way is a Data URI , but with an error

  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) -> cr) -> (forall r. r -> cr) -> Constr -> c (URIRef a) (bound at uri-bytestring/src/URI/ByteString/Types.hs:85:1) (haskell-stack-ghc) 

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

+6
source share
1 answer

You're almost there - the Lift Bytestring instance you're looking for is featured in the th-lift-instances package.

 import Instances.TH.Lift 

Of course, you can also just copy the corresponding instance instead of imposing a dependency.

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

Then, when you enable DeriveLift , StandaloneDeriving , GADTs and TemplateHaskell you can instantiate Lift orphans for all URIRef types that are (transitive) dependent.

 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 

With this add-on, your code is now compiled. In GHCi, I get the following interaction, confirming that everything works.

 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

Just noticed that I never answered the last part of your question.

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

It will be impossible. General programming will not allow you to execute arbitrary verification code at compile time. For this you really need TemplateHaskell . At best, you can use them inside the generated TemplateHaskell code, but that would be optional (there is nothing to do with it).

+10
source

Source: https://habr.com/ru/post/1014822/


All Articles