<div dir="ltr"><div>I am, thanks to an idea from Elliot Cameron, using the Functional Graph Library (FGL) to implement [1] something resembling a hyperggraph, which I'm calling Mindmap, in which relationships can involve any number of things, including other relationships. (By contrast, in a graph, Edges cannot belong to other Edges; only Nodes can.)</div><div><br></div><div>Here are the types:</div><div><br></div><div><font face="monospace, monospace"> -- Exprs (expressions) play Roles in Rels (relationships).</font></div><div><font face="monospace, monospace"> -- A k-ary (Arity k) Rel consists of a k-ary template and k members.</font></div><div><font face="monospace, monospace"> -- Each k-ary Rel emits k+1 Edges toward the other Exprs:</font></div><div><font face="monospace, monospace"> -- one connects it to its RelTplt (relationship template)</font></div><div><font face="monospace, monospace"> -- k more connect it to each of its k RelMbrs (relationship members)</font></div><div><font face="monospace, monospace"> -- The two paragraphs after it will clear up any questions about the next.</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace"> type Mindmap = Gr Expr Role</font></div><div><font face="monospace, monospace"> data Role = RelTplt | RelMbr RelPos</font></div><div><font face="monospace, monospace"> deriving (Show,Read,Eq,Ord)</font></div><div><font face="monospace, monospace"> data Expr = Str String | Tplt Arity [String] | Rel Arity</font></div><div><font face="monospace, monospace"> -- TODO ? deduce the Arity of a Tplt from its [String]</font></div><div><font face="monospace, monospace"> -- TODO ? deduce from the graph the Arity of a Rel</font></div><div><font face="monospace, monospace"> -- rather than carrying it redundantly in the Rel constructor</font></div><div><font face="monospace, monospace"> deriving (Show,Read,Eq,Ord)</font></div><div><font face="monospace, monospace"> type RelPos = Int -- the k members of a k-ary Rel take RelPos values [1..k]</font></div><div><font face="monospace, monospace"> type Arity = Int</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">The following is a Mindmap that represents the expression "dog needs water" using the subexpressions "dog" (a string), "water" (a string), and "_ wants _" (a relationship two things can have, that is a binary Rel):</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace"> -- mkGraph :: Graph gr => [LNode a] -> [LEdge b] -> gr a b</font></div><div><font face="monospace, monospace"> -- that is, mkGraph takes a list of nodes followed by a list of edges</font></div><div><font face="monospace, monospace"> g1 :: Mindmap</font></div><div><font face="monospace, monospace"> g1 = mkGraph</font></div><div><font face="monospace, monospace"> [ (0, Str "dog" )</font></div><div><font face="monospace, monospace"> , (1, stringToTplt "_ wants _" ) -- produces a Tplt with Arity 2</font></div><div><font face="monospace, monospace"> , (3, Str "water" )</font></div><div><font face="monospace, monospace"> , (4, Rel 2 )</font></div><div><font face="monospace, monospace"> ] [ -- "dog wants water"</font></div><div><font face="monospace, monospace"> (4,1, RelTplt) -- Node 1 is the Template for the Rel at Node 4</font></div><div><font face="monospace, monospace"> , (4,0, RelMbr 1) -- Node 0 is the 1st Rel Member of the Rel at Node 4</font></div><div><font face="monospace, monospace"> , (4,3, RelMbr 2) -- Node 3 is the 2nd Rel Member of the Rel at Node 4</font></div><div><font face="monospace, monospace"> ]</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">The next Mindmap encodes the previous statement and a second statement stating that the first is dubious:</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace"> g2 :: Mindmap</font></div><div><font face="monospace, monospace"> g2 = mkGraph</font></div><div><font face="monospace, monospace"> [ (0, Str "dog" )</font></div><div><font face="monospace, monospace"> , (1, stringToTplt "_ wants _" )</font></div><div><font face="monospace, monospace"> , (3, Str "water" )</font></div><div><font face="monospace, monospace"> , (4, Rel 2 )</font></div><div><font face="monospace, monospace"> , (5, stringToTplt "_ is _")</font></div><div><font face="monospace, monospace"> , (6, Str "dubious" )</font></div><div><font face="monospace, monospace"> , (7, Rel 2 )</font></div><div><font face="monospace, monospace"> ] </font></div><div><font face="monospace, monospace"> [ -- "dog wants water" is represented just like it was in g1</font></div><div><font face="monospace, monospace"> (4,1,RelTplt), (4,0, RelMbr 1), (4,3,RelMbr 2),</font></div><div><font face="monospace, monospace"> -- "[dog wants water] is dubious"</font></div><div><font face="monospace, monospace"> (7,5,RelTplt),</font></div><div><font face="monospace, monospace"> (7,4,RelMbr 1), -- Node 4, the first Member of this Rel, is itself a Rel</font></div><div><font face="monospace, monospace"> (7,6,RelMbr 2)</font></div><div><font face="monospace, monospace"> ]</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">I find myself doing a lot of pattern matching that maybe should be type checking instead, to distinguish between the three Expr constructors:</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace">For instance, here is a function that, given a Node at which there is a Rel, returns the Tplt for that Rel:</font></div><div><font face="monospace, monospace"><br></font></div><div><font face="monospace, monospace"> tpltForRelAt :: (MonadError String m) => Mindmap -> Node -> m Expr</font></div><div><font face="monospace, monospace"> tpltForRelAt g rn = do</font></div><div><font face="monospace, monospace"> ir <-isRel g rn</font></div><div><font face="monospace, monospace"> if not ir</font></div><div><font face="monospace, monospace"> then throwError $ "tpltForRelAt: Label of LNode " ++</font></div><div><font face="monospace, monospace"> show rn ++ " is not a Rel."</font></div><div><font face="monospace, monospace"> else return $ fromJust $ lab g</font></div><div><font face="monospace, monospace"> $ head [n | (n,RelTplt) <- lsuc g rn] -- todo ? head is unsafe</font></div><div><font face="monospace, monospace"> -- but is only unsafe if graph takes an invalid state</font></div><div><font face="monospace, monospace"> -- because each Rel should have exactly one Tplt</font></div><div><br></div><div>I had to manually check whether the Expr in question was a Rel. I feel like I'm doing the type system's job.</div><div><br></div><div>Is there a better way?</div><div><br></div><div><br></div><div><div>[1] <a href="https://github.com/JeffreyBenjaminBrown/digraphs-with-text">https://github.com/JeffreyBenjaminBrown/digraphs-with-text</a></div></div>-- <br><div class="gmail_signature"><div dir="ltr">Jeffrey Benjamin Brown</div></div>
</div>