问题
Here "graph" is higher-order function that returns a function with config set in its scope:
(ns bulbs.neo4jserver.graph)
(defn out1
"Test func that simply returns out1."
[config]
"out1")
(defn graph
[config]
(fn [func & args]
(apply func config args)))
You create an instance of graph, which can then be used to call other functions and automatically pass in the config arg:
(def g (graph {:root-uri "http://localhost"}))
(g out1)
;; => "out1"
This works; however, if you require/import graph into another namespace, then you have to prefix each function call with the graph namespace:
(ns bulbs.neo4jserver.junk
(:require [bulbs.neo4jserver.graph :as graph]))
(def g (graph/graph {:root-uri "http://localhost"}))
;; would rather do (g out1)
(g graph/out1)
Instead, I want to explicitly specify the namespace in the apply
function so that users don't have to:
(defn graph
[config]
(fn [func & args]
;; somehow specify the graph namespace here
(apply func config args)))
What's the best way to do this?
回答1:
You can pass symbol instead of function and resolve it in graph
function:
(defn graph
[config]
(fn [func & args]
(apply (ns-resolve 'bulbs.neo4jserver.graph func) config args)))
And call it:
(ns bulbs.neo4jserver.junk
(:require [bulbs.neo4jserver.graph :as graph]))
(def g (graph/graph {:root-uri "http://localhost"}))
(g 'out1)
But g
is not an high-order function any more. It takes symbol, not function. Personally I don't like this approach. Why don't you like specifying namespace? May be you can do what you need with macro too, but I don't know macros well.
EDIT
Don't do it. Use regular functions as explained by @Ankur and @Gert in comments.
回答2:
Not a direct answer to your question, but the general pattern you're using is more common: having a single stateful data structure that holds connection parameters (to a database or another server). Most frameworks turn this around: instead of calling your functions from within the function holding your connection parameters as you do, they have functions that accept the connection data structure as a parameter.
For example, given a database connetion conn
, a typical, fictional database library could look like this (note: examples are simplified for clarity):
(let [conn (make-db-connection :host .... :user ....)]
(read-from-db conn :user))
While using a library for a messaging framework (say, RabbitMQ) could look like this:
(let [conn (make-amqp-connection :host .... :port ...)]
(send-message conn :my-queue "hello world"))
In both situations, there is a single conn
data structure that is used for all subsequent calls to the libraries' functions. In OO languages, you would have a global, stateful object holding the connection (a singleton perhaps in Java land). In Clojure, libraries typically handle this using a with-...
macro, that binds a particular connection to a dynamic var which is used internally:
(with-db-connection (make-db-connection ....)
(read-from-db :user ....))
(with-rabbit-connection (make-rabbitmq-connection ....)
(send-message :my-queue "hello world"))
Here's a (fictional) example that implements this pattern. Assume that a connection is a Java object:
;; a var to hold the connection
(def ^:dynamic *current-connection* nil)
(defmacro with-connection [conn & body]
`(binding [*current-connection* ~conn]
~@body))
;; send-msg is using the connection object bound to
;; the *current-connetion* var
(defn send-msg [msg]
(.sendMessage *current-connection* msg))
;; usage:
(with-connection conn
(send-msg "hello world!"))
If you want to be fancy, you could support both patterns (accepting a connection as a parameter or using the bound connection) by defining the send-msg
function like this:
(defn send-msg [msg & {:keys [connection]
:or {connection *current-connection*}}]
(.sendMessage connection msg))
;; usage with bound connetion:
(with-connection conn
(send-msg "Hello World!"))
;; usage with explicit connection:
(send-msg "Hello World!"
:connection conn)
This version of send-msg
uses the supplied connection, or the bound connection if a connection wasn't specified.
来源:https://stackoverflow.com/questions/10572443/how-do-you-explicitly-specify-a-namespace-when-using-apply-on-a-function-in-cl