I have an application in which I have one function that can be called in some different contexts, and I want some of the functions it calls to operate differently in these different contexts. So, as an example, I might have code that functions like:
(defn foo [a context]
(-> a
inc
(#(bar % context))))
(defn bar [a context]
(cond (= context 1) (* a 2)
(= context 2) (/ a 2)))
Albeit with a bunch of different functions like bar, that are buried inside other functions that themselves do not care about "context".
Because they're buried inside a bunch of other functions (and because I wrote this code for one context, and now am adding others), modifying all of these functions to pass a flag through to all of the relevant 'bar's is a hassle. I don't really like it as a solution, either. Ideally, I'd want to implicitly use the right version of the bar function in each context.
Protocols might solve the issue. I think it would be a huge hassle to rewrite my function using protocols, though, because (in my actual functions) some of the 'bar' functions both use the context and pass it to other functions that use it. So I think I would have to duplicate some of the code (or have different protocols pass flags).
The solution I came up with was to create a namespace for foo, then a separate namespace for each context. In each context's namespace, then, I define a separate namespace. And I change foo so that it calls the version of the bar function residing in the calling namespace. That is:
(ns main)
(defn foo [a]
(-> inc
('bar (ns-map *ns*))))
(ns context-1
(use main))
(defn bar [a]
(* a 2))
(ns context-2
(use main))
(defn bar [a]
(/ a 2))
Then when I call foo from the context-1 namespace, it works as intended. Same for the context-2 namespace.
This basically works, except that since I want to then call the context 1 foo and the context 2 foo from a different namespace, I need to write a wrapper for each namespace in which I go into that namespace for the foo function call then switch back to the namespace I started in. So, in the context-1 ns, I write something like:
(defn context-1-foo [a]
(let [base-ns *ns*]
(in-ns 'context-1)
(let [result (foo a)]
(in-ns (ns-name base-ns))
result)))
This works, and it didn't require a whole lot of changing, but I think it must be non-idiomatic. It also seems like it could be an invitation to have weird bugs.
What's the idiomatic way to do this? Is there a way to do this that, similarly, requires very little change to the code?