From ff04a09a632117d2d26a0408ac388cb05ebad625 Mon Sep 17 00:00:00 2001 From: "Patrick M. Niedzielski" Date: Wed, 20 Mar 2024 03:25:16 +0000 Subject: [PATCH 1/7] Make `VocabularyGenerator` functions Kleisli arrows This patch translates the declaration functions in `Data.RDF.Vocabulary.Generator.VocabularyGenerator` into Kleisli arrows in the `Q` monad. This change will allow us to use the `funD_doc` function to add Haddock documentation to a declaration, which was introduced in Template Haskell version 2.18.0.0. --- .../Generator/VocabularyGenerator.hs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs b/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs index d05124a..13ce9c0 100644 --- a/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs +++ b/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs @@ -50,7 +50,7 @@ genVocabulary :: -- | the filepath of the file containing the schema in RDF Turtle format. String -> Q [Dec] -genVocabulary file = vocabulary <$> runIO (loadGraph file) +genVocabulary file = runIO (loadGraph file) >>= vocabulary loadGraph :: String -> IO (RDF AdjHashMap) loadGraph file = @@ -58,7 +58,7 @@ loadGraph file = Left err -> error $ show err Right rdfGraph -> return rdfGraph -vocabulary :: Rdf a => RDF a -> [Dec] +vocabulary :: Rdf a => RDF a -> Q [Dec] vocabulary graph = let nameDecls = do subject <- nub $ subjectOf <$> triplesOf graph @@ -72,7 +72,7 @@ vocabulary graph = return $ declarePrefix name prefix iri iriDecls = snd <$> nameDecls irisDecl = declareIRIs $ fst <$> nameDecls - in irisDecl : namespaceDecls <> iriDecls + in sequence $ irisDecl : namespaceDecls <> iriDecls toIRI :: Node -> Maybe Text toIRI (UNode iri) = Just iri @@ -87,24 +87,24 @@ unodeFun = VarE $ mkName "Data.RDF.Types.unode" mkPrefixedNSFun :: Exp mkPrefixedNSFun = VarE $ mkName "Data.RDF.Namespace.mkPrefixedNS" -declareIRI :: Name -> Text -> Dec +declareIRI :: Name -> Text -> Q Dec declareIRI name iri = let iriLiteral = LitE . StringL $ T.unpack iri unodeLiteral = AppE unodeFun $ AppE packFun iriLiteral - in FunD name [Clause [] (NormalB unodeLiteral) []] + in funD name [return $ Clause [] (NormalB unodeLiteral) []] -declareIRIs :: [Name] -> Dec +declareIRIs :: [Name] -> Q Dec declareIRIs names = let iriList = ListE (VarE <$> names) - in FunD (mkName "iris") [Clause [] (NormalB iriList) []] + in funD (mkName "iris") [return $ Clause [] (NormalB iriList) []] -- namespace = mkPrefixedNS "ogit" "http://www.purl.org/ogit/" -declarePrefix :: Name -> Text -> Text -> Dec +declarePrefix :: Name -> Text -> Text -> Q Dec declarePrefix name prefix iri = let prefixLiteral = AppE packFun . LitE . StringL . T.unpack $ prefix iriLiteral = AppE packFun . LitE . StringL . T.unpack $ iri namespace = AppE (AppE mkPrefixedNSFun prefixLiteral) iriLiteral - in FunD name [Clause [] (NormalB namespace) []] + in funD name [return $ Clause [] (NormalB namespace) []] iriToName :: Text -> Maybe Name iriToName iri = mkName . T.unpack . escape <$> (lastMay . filter (not . T.null) . T.split (`elem` separators)) iri From bd44a8cbd7dc72878894b2e303f1450059da1351 Mon Sep 17 00:00:00 2001 From: "Patrick M. Niedzielski" Date: Wed, 20 Mar 2024 05:06:49 +0000 Subject: [PATCH 2/7] Bump Stackage LTS version Template Haskell 2.18.0 introduces the ability to add Haddock documentation to declarations that are generated within the `Q` monad. We tried adding this as an `extra-dep` within the `stack.yaml` file, but because the Stackage LTS version is so old (over three years!), and because several of our dependency packages specify incorrect and unnecessary upper bounds on the Template Haskell version they work with, doing so would involve pinning many more dependencies than we already do. This patch takes the route instead of bumping the Stackage LTS version to the minimum version that includes Template Haskell 2.18. --- README.md | 2 +- rdf4h.cabal | 4 ++-- stack.yaml | 13 +------------ 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 4187148..8affe44 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ For details see the GitHub project page: http://robstewart57.github.io/rdf4h/ -Supports GHC versions from 8.0.2 (stackage lts-9) to 8.8.3 (stackage lts-16.0). +Supports GHC versions from 9.2.5 (stackage lts-20.11). ### Development with Nix and direnv diff --git a/rdf4h.cabal b/rdf4h.cabal index 51851c2..f61183a 100644 --- a/rdf4h.cabal +++ b/rdf4h.cabal @@ -21,7 +21,7 @@ cabal-version: >= 1.10 build-type: Simple category: RDF stability: stable -tested-with: GHC==8.0.2, GHC==8.2.2, GHC==8.4.3, GHC==8.6.5, GHC==8.8.3 +tested-with: GHC==9.2.5 extra-tmp-files: test extra-source-files: examples/ParseURLs.hs , examples/ESWC.hs @@ -97,7 +97,7 @@ library , selective , html-entities , xeno - , template-haskell + , template-haskell >= 2.18.0 other-modules: Text.RDF.RDF4H.XmlParser.Xmlbf , Text.RDF.RDF4H.XmlParser.Xeno if impl(ghc < 7.6) diff --git a/stack.yaml b/stack.yaml index cd37b64..21378ed 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,18 +1,7 @@ -resolver: lts-16.6 +resolver: lts-20.11 packages: - '.' extra-deps: -- algebraic-graphs-0.5 -- unordered-containers-0.2.10.0 -- selective-0.3 -- html-entities-1.1.4.3 -- mmorph-1.1.3 -- exceptions-0.10.4 -- semigroups-0.18.3 -- xeno-0.3.5.2 -- Cabal-3.2.0.0@sha256:d0d7a1f405f25d0000f5ddef684838bc264842304fd4e7f80ca92b997b710874,27320 -- parsec-3.1.14.0 -- text-1.2.4.0 # for weeder tool # https://github.com/ndmitchell/weeder From f5d15d801b8be50f81cd04b4d1e126a223c35d46 Mon Sep 17 00:00:00 2001 From: "Patrick M. Niedzielski" Date: Wed, 20 Mar 2024 05:06:55 +0000 Subject: [PATCH 3/7] Document namespace prefixes This patch adds simple Haddock documentation for namespace prefixes generated from an RDFS or OWL file. --- src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs b/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs index 13ce9c0..e5760a7 100644 --- a/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs +++ b/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs @@ -104,7 +104,9 @@ declarePrefix name prefix iri = let prefixLiteral = AppE packFun . LitE . StringL . T.unpack $ prefix iriLiteral = AppE packFun . LitE . StringL . T.unpack $ iri namespace = AppE (AppE mkPrefixedNSFun prefixLiteral) iriLiteral - in funD name [return $ Clause [] (NormalB namespace) []] + in funD_doc name [return $ Clause [] (NormalB namespace) []] + (Just $ "Namespace prefix for \\<<" <> T.unpack iri <> ">\\>.") + [Nothing] iriToName :: Text -> Maybe Name iriToName iri = mkName . T.unpack . escape <$> (lastMay . filter (not . T.null) . T.split (`elem` separators)) iri From 61bb2f600b72586debdb591d5e2dba642d07a0f4 Mon Sep 17 00:00:00 2001 From: "Patrick M. Niedzielski" Date: Wed, 20 Mar 2024 05:10:07 +0000 Subject: [PATCH 4/7] Document `iris` This patch adds documentation for the `iris` value generated from an RDFS or OWL vocabulary. --- src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs b/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs index e5760a7..d969ae5 100644 --- a/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs +++ b/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs @@ -96,7 +96,9 @@ declareIRI name iri = declareIRIs :: [Name] -> Q Dec declareIRIs names = let iriList = ListE (VarE <$> names) - in funD (mkName "iris") [return $ Clause [] (NormalB iriList) []] + in funD_doc (mkName "iris") [return $ Clause [] (NormalB iriList) []] + (Just $ "All IRIs in this vocabulary.") + [Nothing] -- namespace = mkPrefixedNS "ogit" "http://www.purl.org/ogit/" declarePrefix :: Name -> Text -> Text -> Q Dec From e99f5c7b0de169b729f5bca2efafba40f394a91d Mon Sep 17 00:00:00 2001 From: "Patrick M. Niedzielski" Date: Wed, 20 Mar 2024 05:10:16 +0000 Subject: [PATCH 5/7] Add ability to document IRIs This patch introduces the ability to document IRIs generated from an RDFS or OWL vocabulary. It does not provide any documentation for such IRIs, though, so there should be no change in the generated documentation after applying this patch. --- .../RDF/Vocabulary/Generator/VocabularyGenerator.hs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs b/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs index d969ae5..3496d4a 100644 --- a/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs +++ b/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs @@ -64,7 +64,7 @@ vocabulary graph = subject <- nub $ subjectOf <$> triplesOf graph iri <- maybeToList $ toIRI subject name <- maybeToList $ iriToName iri - return (name, declareIRI name iri) + return (name, declareIRI name iri Nothing) (PrefixMappings prefixMappings') = prefixMappings graph namespaceDecls = do (prefix, iri) <- M.toList prefixMappings' @@ -87,11 +87,13 @@ unodeFun = VarE $ mkName "Data.RDF.Types.unode" mkPrefixedNSFun :: Exp mkPrefixedNSFun = VarE $ mkName "Data.RDF.Namespace.mkPrefixedNS" -declareIRI :: Name -> Text -> Q Dec -declareIRI name iri = +declareIRI :: Name -> Text -> Maybe Text -> Q Dec +declareIRI name iri comment = let iriLiteral = LitE . StringL $ T.unpack iri unodeLiteral = AppE unodeFun $ AppE packFun iriLiteral - in funD name [return $ Clause [] (NormalB unodeLiteral) []] + in funD_doc name [return $ Clause [] (NormalB unodeLiteral) []] + (T.unpack <$> comment) + [Nothing] declareIRIs :: [Name] -> Q Dec declareIRIs names = From 09a40346d33bf9feef333e3d9a43eb0bbf6dfa69 Mon Sep 17 00:00:00 2001 From: "Patrick M. Niedzielski" Date: Wed, 20 Mar 2024 05:53:44 +0000 Subject: [PATCH 6/7] Document IRIs using `rdfs:comment` This patch reads all `rdfs:comment`s for a given subject, concatenates them together, separated by newlines, and uses this value as the Haddock documentation for that subject. Not all vocabularies in this library are fully documented using `rdfs:comment`, though, so there remain some generated subjects that lack documentation. This is for two reasons. First, for those nodes that lack any documentation at all, this patch elects to remain faithful to the underlying vocabulary and keep these nodes undocumented. Second, some vocabularies, like SKOS, use their own predicate for documentation; this patch chooses to only read the standard `rdfs:comment`. Additional documentation vocabularies can be added later if required. --- .../Generator/VocabularyGenerator.hs | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs b/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs index 3496d4a..4286ffa 100644 --- a/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs +++ b/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs @@ -6,19 +6,23 @@ module Data.RDF.Vocabulary.Generator.VocabularyGenerator ) where +import Control.Monad (join) import Data.Char (isLower) import Data.List (nub) import qualified Data.Map as M import Data.Maybe (maybeToList) import Data.RDF ( AdjHashMap, - Node (UNode), + LValue (..), + Node (..), PrefixMappings (PrefixMappings), RDF, Rdf, TurtleParser (TurtleParser), + objectOf, parseFile, prefixMappings, + query, subjectOf, triplesOf, ) @@ -64,7 +68,11 @@ vocabulary graph = subject <- nub $ subjectOf <$> triplesOf graph iri <- maybeToList $ toIRI subject name <- maybeToList $ iriToName iri - return (name, declareIRI name iri Nothing) + let comment = combineComments . + sequenceA . + fmap (nodeToComment . objectOf) $ + query graph (Just subject) (Just rdfsCommentNode) Nothing + return (name, declareIRI name iri comment) (PrefixMappings prefixMappings') = prefixMappings graph namespaceDecls = do (prefix, iri) <- M.toList prefixMappings' @@ -87,6 +95,23 @@ unodeFun = VarE $ mkName "Data.RDF.Types.unode" mkPrefixedNSFun :: Exp mkPrefixedNSFun = VarE $ mkName "Data.RDF.Namespace.mkPrefixedNS" +nodeToComment :: Node -> Maybe Text +nodeToComment (UNode uri) = Just $ "See \\<<" <> uri <> ">\\>." +nodeToComment (BNode _) = Nothing +nodeToComment (BNodeGen _) = Nothing +nodeToComment (LNode (PlainL l)) = Just l +nodeToComment (LNode (PlainLL l _)) = Just l +nodeToComment (LNode (TypedL l _)) = Just l + +combineComments :: Maybe [Text] -> Maybe Text +combineComments = join . fmap combineComments' + where + combineComments' [] = Nothing + combineComments' comments = Just . T.intercalate "\n" $ comments + +rdfsCommentNode :: Node +rdfsCommentNode = UNode "http://www.w3.org/2000/01/rdf-schema#comment" + declareIRI :: Name -> Text -> Maybe Text -> Q Dec declareIRI name iri comment = let iriLiteral = LitE . StringL $ T.unpack iri From c99e94bdbc37e91993a1a03057e03d2b77efdf99 Mon Sep 17 00:00:00 2001 From: "Patrick M. Niedzielski" Date: Wed, 20 Mar 2024 06:24:01 +0000 Subject: [PATCH 7/7] Sort generated subjects alphabetically This patch sorts all IRI subject declarations that are generated from an RDFS or OWL input alphabetically. This has the side effect of placing all classes first and all predicates after. --- src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs b/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs index 4286ffa..1cfca09 100644 --- a/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs +++ b/src/Data/RDF/Vocabulary/Generator/VocabularyGenerator.hs @@ -8,7 +8,7 @@ where import Control.Monad (join) import Data.Char (isLower) -import Data.List (nub) +import Data.List (nub, sortBy) import qualified Data.Map as M import Data.Maybe (maybeToList) import Data.RDF @@ -78,7 +78,7 @@ vocabulary graph = (prefix, iri) <- M.toList prefixMappings' let name = mkName . T.unpack . escape $ prefix <> "NS" return $ declarePrefix name prefix iri - iriDecls = snd <$> nameDecls + iriDecls = fmap snd . sortBy (\x y -> fst y `compare` fst x) $ nameDecls irisDecl = declareIRIs $ fst <$> nameDecls in sequence $ irisDecl : namespaceDecls <> iriDecls