I'm writing (another) basic graph library, and would like to treat inputs depending on the type of the graph. A graph can be
- Directed, in which case edges are vectors. Otherwise, edges are sets; - Looped, allowing edges from a node to itself; - Pseudo (or multi), allowing multiples edges between the same endpoints; and - Hyper, allowing edges with more than two vertices. To illustrate better these characteristics you can think of a scientific publication network as a directed, looped, pseudo-hypergraph. Vertices are authors, and edges are articles containing multiple researchers (hyper) who can publish alone (looped). There are multiple articles between the same researchers (pseudo) and in some contexts author order matters (directed). Now, I've created a flowchart <http://imgur.com/IdgsGFG> to decide if an edge should be conjed in a graph :edges entry, that leads to the following straightforward function: (defn add-edge ([graph v1 v2 & vs] (add-edge graph (concat [v1 v2] vs))) ([graph edge] (if (and (multi? graph) (not= 2 (count edge))) graph (if (and (looped? graph) (not (distinct? edge))) graph (let [e (if (directed? edge) (vec edge) (set edge))] (update-in graph [:edges] conj e)))))) That looks ugly and a pattern that could propagate in a codebase. So I tried to factor out multimethods from it, and ended with the following: (defmulti ^:private add-edge0 (fn [g e] (hyper? g))) (defmulti ^:private add-edge1 (fn [g e] (looped? g))) (defmulti ^:private add-edge2 (fn [g e] (directed? g))) (defn ^:private add-edge3 [g e] (update-in g [:edges] conj e)) (defmethod add-edge0 :hyper [g e] (add-edge1 g e)) (defmethod add-edge0 :default [g e] (if (= 2 (count e)) (add-edge1 g e) g)) (defmethod add-edge1 :looped [g e] (add-edge2 g e)) (defmethod add-edge1 :default [g e] (if (distinct? e) (add-edge2 g e) g)) (defmethod add-edge2 :directed [g e] (add-edge3 g (vec e))) (defmethod add-edge2 :default [g e] (add-edge3 g (set e))) (defn add-edge ([g v1 v2 & vs] (add-edge g (concat [v1 v2] vs))) ([g edge] (add-edge0 g edge))) That doesn't look much better, as the amount of boilerplate increased, but at least the concerns for each type are separated. Do you have any suggestions on how to improve this design? Thanks for any consideration! Bruno Kim Medeiros Cesar Engenheiro de Computação Pesquisador em Redes Complexas www.brunokim.com.br -- -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.