I'm writing this program that is like a web crawler for online forums. For each forum I crawl, I need to do the same thing:
- login
- find the boards
- find the posts
- find the permalink to the post
- find username of the person who made the post
- etc.
Now while this same logic needs to occur for each forum, the implementation for each forum is different. For example, the inputs of each login form is different per forum. One forum may have a field named "username" the other may have a field named "user". Some of these steps may have default implementations. For example, the default implementation of login is to not do anything (because you don't have to login to some forums to crawl it).
What I've done is created a function with all these steps in it named crawl-forum but the implementations are abstract and implemented elsewhere. My question is, what's the best way to get crawl-forum to use these implementations?
What I've tried
1) Config map
Here's what I've tried so far. I added a new argument to the crawl-forum function called configs. It's a map data structure that looks like this:
{ :login login-function
:find-boards find-boards-function
...
}
The code that calls crawl-forum is responsible for populating that map. What I don't like about this is the configs need to be passed throughout the crawl-forum code. It adds a new parameter everywhere. Also, I've got some lame, ad-hoc code for handling default implementations.
2) Multimethods
I talked on irc about this and someone gave me the idea that I should use multimethods for this instead since it's really polymorphic behavior. They look like this:
(defn get-site-key [& args] (first args))
(defmulti login get-site-key)
(defmethod login :default [site-key cookie-store] nil)
Then the client code has to define its own multimethods on the outside:
(defmethod login :forum-1 [site-key cookie-store] (do-something cookie-store))
What I don't like about this is that just like the config, I have to pass the site-key in to the crawl-forum function and that site-key still has to be passed around everywhere inside. Also, every defmethod has to be passed its own site-key back as a parameter, but none of them will ever use it. It's simply a necessary argument to do dispatch on. It's really hard for me to find a thorough multimethod tutorial though, so if there's a smarter way to do this, let me know.
Is there a 3rd option that's even better? Is there a better way to be using multimethods? Let me know, thanks.
get, theormacro, and the:ordirective in destructing to alleviate your "lame, ad-hoc code for handling default implementations"? If using multimethods, you have the default as well. - A. Webb:orwas defined for each function right before it was used. Maybe I should have done this all in one place. - Daniel Kaplan