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