【发布时间】:2017-02-21 21:14:49
【问题描述】:
我有一个应用程序,其中我有一个可以在某些不同上下文中调用的函数,并且我希望它调用的某些函数在这些不同上下文中以不同方式运行。所以,作为一个例子,我可能有代码功能类似于:
(defn foo [a context]
(-> a
inc
(#(bar % context))))
(defn bar [a context]
(cond (= context 1) (* a 2)
(= context 2) (/ a 2)))
尽管有一堆不同的函数,比如 bar,它们隐藏在其他函数中,它们本身并不关心“上下文”。
因为它们隐藏在一堆其他函数中(而且因为我为一个上下文编写了这段代码,现在正在添加其他函数),所以修改所有这些函数以将标志传递给所有相关的 'bar's麻烦。我也不喜欢它作为解决方案。理想情况下,我想在每个上下文中隐式使用正确版本的 bar 函数。
协议可能会解决问题。不过,我认为使用协议重写我的函数会很麻烦,因为(在我的实际函数中)一些“bar”函数既使用上下文,又将其传递给使用它的其他函数。所以我想我必须复制一些代码(或者让不同的协议传递标志)。
我想出的解决方案是为 foo 创建一个命名空间,然后为每个上下文创建一个单独的命名空间。然后,在每个上下文的名称空间中,我定义了一个单独的名称空间。我更改了 foo 以便它调用驻留在调用命名空间中的 bar 函数的版本。那就是:
(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))
然后,当我从 context-1 命名空间调用 foo 时,它会按预期工作。与 context-2 命名空间相同。
这基本上是可行的,除了因为我想从 不同的 命名空间调用上下文 1 foo 和上下文 2 foo,我需要为我进入的每个命名空间编写一个包装器用于 foo 函数调用的命名空间然后切换回我开始时使用的命名空间。因此,在 context-1 ns 中,我编写如下内容:
(defn context-1-foo [a]
(let [base-ns *ns*]
(in-ns 'context-1)
(let [result (foo a)]
(in-ns (ns-name base-ns))
result)))
这行得通,并且不需要进行大量更改,但我认为它必须是非惯用的。似乎也可能是邀请出现奇怪的错误。
这样做的惯用方法是什么?有没有办法做到这一点,同样,只需要对代码进行很少的更改?
【问题讨论】:
标签: clojure