Greetings all! I’m developing an application which takes files as input and processes these differently based on the file’s metadata. I imagine this is pretty common among users of functional programming, and it’s not the first time I have written a program with this kind of architecture. However, I’m wondering which of the myriad of ways to do this is most optimal, and this is where you come in!
Out of the options below, what do you think are the pros and cons of them? Do you have any ways that you’ve developed that you think would work well? I’m keen to hear your thoughts
For sake of example, consider an application which reads data about what sort of email notification to send a user.
Option 1 - ‘function pointers’
There’s a number of functions, emails/send-promotion!
etc., that should be called to respond to different events. You have a map from event type to function…
(def dispatch-map
{:sale-confirmation emails/send-sales-confirmation!
:promotion emails/send-promotion!
:welcome emails/send-welcome!})
And when the event is received, the right function is called:
(defn dispatch-function [event]
(when-some [function (resolve (dispatch-map event))]
(function)))
Of course, the dispatch-map
could also be an argument to dispatch-function
rather than a global binding.
Option 2 - pattern matching
There’s the same set of functions, but no dispatch-map
. Instead, the functions are called directly from the dispatch-function
:
(defn dispatch-function-alt [event]
(condp = event
:sale-confirmation (emails/send-sales-confirmation!)
:promotion (emails/send-promotion!)
:welcome (emails/send-welcome!)
nil))
Option 3 - multimethods
There’s just a single call to defmulti
to set up the dispatching:
(defmulti dispatch identity)
Then, there are calls to defmethod
to set up the function for each value:
(defmethod dispatch :sales [_] (
;; the body of emails/send-sales-confirmation!
))
(defmethod dispatch :promotion [_] (
;; the body of emails/send-promotion!
))
(defmethod dispatch :welcome [_] (
;; the body of emails/send-welcome!
))
(defmethod dispatch :default [_] nil)
Looking forward to hearing what you think!