Disclaimer: Below is basically a meaningless exercise in the technique of "node binding." Fgl is the way to go if you want to actually use your graphics. However, if you are interested in how you can functionally represent circular data structures, read on.
It's pretty easy to imagine a graph in Haskell!
-- a directed graph data Vertex ab = Vertex { vdata :: a, edges :: [Edge ab] } data Edge ab = Edge { edata :: b, src :: Vertex ab, dst :: Vertex ab } -- My graph, with vertices labeled with strings, and edges unlabeled type Myvertex = Vertex String () type Myedge = Edge String () -- A couple of helpers for brevity e :: Myvertex -> Myvertex -> Myedge e = Edge () v :: String -> [Myedge] -> Myvertex v = Vertex -- This is a full 5-graph mygraph5 = map vv [ "one", "two", "three", "four", "five" ] where vv s = let vk = vs (zipWith e (repeat vk) mygraph5) in vk
This is a cyclic, finite, recursive, purely functional data structure. Not very efficient or beautiful, but look, ma, there are no pointers! Here's an exercise: include incoming edges at the top
data Vertex ab = Vertex {vdata::a, outedges::[Edge ab], inedges::[Edge ab]}
It is easy to construct a complete graph that has two (indistinguishable) copies of each edge:
mygraph5 = map vv [ "one", "two", "three", "four", "five" ] where vv s = let vks = repeat vk vk = vs (zipWith e vks mygraph5) (zipWith e mygraph5 vks) in vk
but try to build one that has one copy of each! (Imagine that there are some expensive calculations in e v1 v2 ).