rdf-0.1.0.1: src/Data/RDF/Graph.hs
{-|
Module : Data.RDF.Graph
Description : Representation and Incremental Processing of RDF Data
Copyright : Travis Whitaker 2016
License : MIT
Maintainer : pi.boy.travis@gmail.com
Stability : Provisional
Portability : Portable
This module provides conversion between RDF triples and @fgl@ graphs. Naturally
these functions will force the entire graph into memory.
-}
{-# LANGUAGE DeriveGeneric
, DeriveAnyClass
#-}
module Data.RDF.Graph (
-- FGL Supporting Types
GNode(..)
, GEdge
-- * Conversion to FGL Graphs
, rdfGraph
, triplesGraph
-- * Conversion from FGL Graphs
, graphRDF
, graphTriples
) where
import Control.DeepSeq
import qualified Data.Graph.Inductive.Graph as G
import qualified Data.Graph.Inductive.NodeMap as G
import Data.Maybe
import Data.RDF.Types
import GHC.Generics
-- | An RDF 'Subject' or 'Object' as a 'G.Graph' node. This common
-- representation is necessary because the 'Object' of one 'Triple' might be
-- the 'Subject' of another.
data GNode = IRIGNode !IRI
| BlankGNode !BlankNode
| LiteralGNode !Literal
deriving ( Eq
, Ord
, Read
, Show
, Generic
, NFData
)
-- | A 'G.Graph' edge is an RDF 'Predicate'.
type GEdge = Predicate
-- | Convert a 'Subject' to a 'GNode'.
subjectNode :: Subject -> GNode
subjectNode (IRISubject i) = IRIGNode i
subjectNode (BlankSubject b) = BlankGNode b
-- | Convert an 'Object' to a 'GNode'.
objectNode :: Object -> GNode
objectNode (IRIObject i) = IRIGNode i
objectNode (BlankObject b) = BlankGNode b
objectNode (LiteralObject l) = LiteralGNode l
-- | Convert a 'GNode' to a 'Subject'. This will fail if the 'GNode' contains a
-- 'Literal'.
nodeSubject :: GNode -> Either String Subject
nodeSubject (IRIGNode i) = Right (IRISubject i)
nodeSubject (BlankGNode b) = Right (BlankSubject b)
nodeSubject _ = Left "nodeSubject: subject must IRI or blank node."
-- | Convert a 'GNode' to an 'Object'.
nodeObject :: GNode -> Object
nodeObject (IRIGNode i) = IRIObject i
nodeObject (BlankGNode b) = BlankObject b
nodeObject (LiteralGNode l) = LiteralObject l
-- | Convert an 'RDFGraph' into a 'G.DynGraph' and 'G.NodeMap'. The 'graphLabel'
-- is discarded.
rdfGraph :: G.DynGraph g => RDFGraph -> (g GNode GEdge, G.NodeMap GNode)
rdfGraph (RDFGraph _ ts) = triplesGraph ts
-- | Convert a list of 'Triple's into a 'G.DynGraph' and a 'G.NodeMap'.
triplesGraph :: G.DynGraph g => [Triple] -> (g GNode GEdge, G.NodeMap GNode)
triplesGraph triples = G.mkMapGraph nodes edges
where (nodes, edges) = go ([],[]) triples
go (ns, es) [] = (ns, es)
go (ns, es) (Triple s p o:ts) = let s' = subjectNode s
o' = objectNode o
in go (s':o':ns, (s', o', p):es) ts
-- | Convert a 'G.Graph' into an 'RDFGraph'. This will fail if the graph
-- contains any 'LiteralGNode's with an outward degree greater than zero,
-- since such a graph is illegal in RDF.
graphRDF :: G.Graph g => Maybe IRI -> g GNode GEdge -> Either String RDFGraph
graphRDF l = (RDFGraph l <$>) . graphTriples
-- | Convert a 'G.Graph' into a list of 'Triple's. This will fail if the graph
-- contains any 'LiteralGNode's with an outward degree greater than zero,
-- since such a graph is illegal in RDF.
graphTriples :: G.Graph g => g GNode GEdge -> Either String [Triple]
graphTriples g = go (G.labEdges g)
-- The use of fromJust is safe here, since labEdges will never return
-- an edge to a node not present in the graph.
where go [] = Right []
go ((si, oi, p):ts) = let s = nodeSubject (fromJust (G.lab g si))
o = nodeObject (fromJust (G.lab g oi))
in ((\s' -> (Triple s' p o:)) <$> s) <*> go ts