In a number of occasions I have a collection of functions that I'd like to implement in different ways. The most obvious example of this would be to abstract from specific databases. In an object-oriented language you would use an interface for this:
interface DB {
ResultSet query(String query);
void persist(Object o);
...
}
In speudo code I would want to do something like this:
(ns dbbackend)
(abstractfn query [q])
(abstractfn persist! [o])
And then implementations for each database:
(ns dbbackend.mysql :implements dbbackend)
(defn query [q] ...)
(defn persist! [o] ...)
It is not entirely clear to me what the best practice is to do something similar in a functional language, specifically Clojure. Should I use multi-methods for this?
Now that version 1.1 of Clojure has been released maybe it's time to take a look into the future.
Datatypes and protocols, which are currently only available in the new master branch on github , might be exactly what you are looking for.
(defprotocol DB
(query [backend query])
(persist [backend object]))
(deftype MySQLBackend []
DB
(query [query] ...)
(persist [object] ...))
For pre-protocol Clojure versions:
The interface:
(ns dbbackend)
(defmulti query
{:arglists '([c q])}
suitable-dispatch-fn)
(defmulti persist!
{:arglists '([c o])}
suitable-dispatch-fn)
The implementation:
(ns dbbackend.mysql
(:requires dbbackend))
(defmethod query com.mysql.jdbc.Connection
[c q]
...)
(defmethod persist! com.mysql.jdbc.Connection
[c o]
...)
The usage:
(ns user
(:require dbbackend dbbackend.mysql))
(def mysql-connection (connect-to-mysql))
(query mysql-connection some-query)
You can find a real-world example of this approach under the hood of ClojureQL.
Related
I want to change certain key's in a large map in clojure.
These key's can be present at any level in the map but will always be within a required-key
I was looking at using camel-snake-kebab library but need it to change only a given set of keys in the required-key map. It doesn't matter if the change is made in json or the map
(def my-map {:allow_kebab_or-snake {:required-key {:must_be_kebab ""}}
:allow_kebab_or-snake2 {:optional-key {:required-key {:must_be_kebab ""}}}})
currently using /walk/postwalk-replace but fear it may change keys not nested within the :required-key map
(walk/postwalk-replace {:must_be_kebab :must-be-kebab} my-map))
ummmm.. could you clarify: do you want to change the keys of the map?! or their associated values?
off-topic: your map above is not correct (having two identical keys :allow_kebab_or_snake - i-m assuming you're just underlining the point and not showing the actual example :))
postwalk-replace WILL replace any occurrence of the key with the value.
so if you know the exact map struct you could first select your sub-struct with get-in and then use postwalk-replace :
(walk/postwalk-replace {:must_be_kebab :mus-be-kebab}
(get-in my-map [:allow_kebab_or_snake :required-key]))
But then you'll have to assoc this into your initial map.
You should also consider the walk function and construct your own particular algorithm if the interleaved DS is too complex.
Here is a solution. Since you need to control when the conversion does/doesn't occur, you can't just use postwalk. You need to implement your own recursion and change the context from non-convert -> convert when your condition is found.
(ns tst.clj.core
(:use clj.core clojure.test tupelo.test)
(:require
[clojure.string :as str]
[clojure.pprint :refer [pprint]]
[tupelo.core :as t]
[tupelo.string :as ts]
))
(t/refer-tupelo)
(t/print-versions)
(def my-map
{:allow_kebab_or-snake {:required-key {:must_be_kebab ""}}
:allow_kebab_or-snake2 {:optional-key {:required-key {:must_be_kebab ""}}}})
(defn children->kabob? [kw]
(= kw :required-key))
(defn proc-child-maps
[ctx map-arg]
(apply t/glue
(for [curr-key (keys map-arg)]
(let [curr-val (grab curr-key map-arg)
new-ctx (if (children->kabob? curr-key)
(assoc ctx :snake->kabob true)
ctx)
out-key (if (grab :snake->kabob ctx)
(ts/kw-snake->kabob curr-key)
curr-key)
out-val (if (map? curr-val)
(proc-child-maps new-ctx curr-val)
curr-val)]
{out-key out-val}))))
(defn nested-keys->snake
[arg]
(let [ctx {:snake->kabob false}]
(if (map? arg)
(proc-child-maps ctx arg)
arg)))
The final result is shown in the unit test:
(is= (nested-keys->snake my-map)
{:allow_kebab_or-snake
{:required-key
{:must-be-kebab ""}},
:allow_kebab_or-snake2
{:optional-key
{:required-key
{:must-be-kebab ""}}}} ))
For this solution I used some of the convenience functions in the Tupelo library.
Just a left of field suggestion which may or may not work. This is a problem that can come up when dealing with SQL databases because the '-' is seen as a reserved word and cannot be used in identifiers. However, it is common to use '-' in keywords when using clojure. Many abstraction layers used when working with SQL in clojure take maps as arguments/bindings for prepared statements etc.
Ideally, what is needed is another layer of abstraction which converts between kebab and snake case as needed depending on the direction you are going i.e. to sql or from sql. The advantage of this aproach is your not walking through maps making conversions - you do the conversion 'on the fly" when it is needed.
Have a look at https://pupeno.com/2015/10/23/automatically-converting-case-between-sql-and-clojure/
Although the below example seems a bit strange, it's because I'm trying to reduce a fairly large problem I've got at present to a minimal example. I'm struggling to work out how to call into multimethods when they're sitting behind a couple of abstraction layers and the defmulti and corresponding defmethods are defined in multiple namespaces. I really feel like I'm missing something obvious here...
Suppose I've got the following scenario:
I purchase stuff from a variety of suppliers, via their own proprietary interfaces
I want to implement a common interface to talk to each of those suppliers
I want to be able to purchase different items from different suppliers
Using Clojure, the recommended ways of implementing a common interface would be via protocols or multimethods. In this case, as I'm switching based on the value of the supplier, I think the best way to handle the situation I'm describing below is via multimethods (but I could be wrong).
My multimethod definitions would look something like this, which defines a common interface I want to use to talk to every supplier's APIs:
(ns myapp.suppliers.interface)
(defmulti purchase-item :supplier)
(defmulti get-item-price :supplier)
For each supplier, I probably want something like:
(ns myapp.suppliers.supplier1
(:require [myapp.suppliers.interface :as supplier-api]))
(defmethod purchase-item :supplier1 [item quantity] ...)
(defmethod get-item-price :supplier1 [item] ...)
and
(ns myapp.suppliers.supplier2
(:require [myapp.suppliers.interface :as supplier-api]))
(defmethod purchase-item :supplier2 [item quantity] ...)
(defmethod get-item-price :supplier2 [item] ...)
So far, no problem
Now to my code which calls these abstracted methods, which I assume looks something like:
(ns myapp.suppliers.api
(:require [myapp.suppliers.supplier1 :as supplier1]
[myapp.suppliers.supplier2 :as supplier2])
(defn buy-something
[supplier item quantity]
(purchase-item [supplier item quantity])
(defn price-something
[supplier item]
(get-item-price [supplier item])
This is starting to look a bit ... ugly. Every time I implement a new supplier's API, I'll need to change myapp.suppliers.api to :require that new supplier's methods and recompile.
Now I'm working at the next level up, and I want to buy a widget from supplier2.
(ns myapp.core
(:require [myapp.suppliers.api :as supplier])
(def buy-widget-from-supplier2
(buy-something :supplier2 widget 1)
This can't work, because :supplier2 hasn't been defined anywhere in this namespace.
Is there a more elegant way to write this code? In particular, in myapp.core, how can I buy-something from :supplier2?
Initial notes
It's hard to tell if you mixed up some things in the process of simplifying the example, or if they weren't quite right out of the gate. For an example of what I'm referring to, consider purchase-item, though the issues are similar for get-item-price:
The defmulti call is a single-argument function
The defmethod calls each take two arguments
The call in buy-something passes a vector to purchase-item, but looking up the :supplier keyword in a vector will always return nil
Your concerns
Every time I implement a new supplier's API, I'll need to change myapp.suppliers.api to :require that new supplier's methods and recompile.
If you require the myapp.suppliers.interface namespace myapp.suppliers.api, the problem can be avoided
This can't work, because :supplier2 hasn't been defined anywhere in this namespace.
Simply put, this will work. :)
Is there a more elegant way to write this code? In particular, in myapp.core, how can I buy-something from :supplier2?
Certainly, but this solution is going to make some assumption based on the ambiguities in the Initial notes.
Without straying too far from your original design, here's a fully-working example of how I interpret what you were trying to achieve:
myapp.suppliers.interface
(ns myapp.suppliers.interface)
(defmulti purchase-item (fn [supplier item quantity] supplier))
myapp.suppliers.supplier1
(ns myapp.suppliers.supplier1
(:require [myapp.suppliers.interface :as supplier-api]))
(defmethod supplier-api/purchase-item :supplier1 [supplier item quantity]
(format "Purchasing %dx %s from %s" quantity (str item) (str supplier)))
myapp.suppliers.supplier2
(ns myapp.suppliers.supplier2
(:require [myapp.suppliers.interface :as supplier-api]))
(defmethod supplier-api/purchase-item :supplier2 [supplier item quantity]
(format "Purchasing %dx %s from %s" quantity (str item) (str supplier)))
myapp.suppliers.api
(ns myapp.suppliers.api
(:require [myapp.suppliers.interface :as interface]))
(defn buy-something [supplier item quantity]
(interface/purchase-item supplier item quantity))
myapp.core
(ns myapp.core
(:require [myapp.suppliers.api :as supplier]))
(def widget {:id 1234 :name "Monchkin"})
(supplier/buy-something :supplier1 widget 15)
;;=> "Purchasing 15x {:id 1234, :name \"Monchkin\"} from :supplier1"
(supplier/buy-something :supplier2 widget 3)
;;=> "Purchasing 3x {:id 1234, :name \"Monchkin\"} from :supplier2"
As you can see, the supplier/buy-something calls propagate to the appropriate multimethod implementations. Hopefully this helps you get where you were trying to go.
According to Om Next's documentation:
query->ast
(om.next/query->ast '[(:foo {:bar 1})])
Given a query expression return the AST.
ast->query
(om.next/ast->query ast)
Given a query expression AST, unparse it into a query expression.
Question: Why would one need these functions? That is, why would one need to directly manipulate a query abstract syntax tree (which I'm assuming are clojure maps that represent a query tree, along with some meta data) in om next?
There are some scenarios where you need to manipulate the query ast directly. In remote parsing mode, the parser expects your read functions to return either {:remote-name true } or a (possibly modified) {:remote-name AST-node} (which comes in as :ast in env). Most often you'll have to modify the AST to restructure it or add some data.
Example 1:
You have a query: [{:widget {:list [:name :created]}}]
The :widget part is pure UI related, your server doesn't need to know it exists, it only cares/knows about the :list.
Basically you'll have to modify the AST in the parser:
(defmethod read :list
[{:keys [ast query state]} key _ ]
(let [st #state]
{:value (om/db->tree query (get st key) st)
:remote (assoc ast :query-root true)}))
If you use om/process-rootsin your send function, it'll pick up the :query-root out of the ast and rewrite the query from [{:widget {:list [:name :created]}}] to [{:list [:name :created]}].
Example 2:
Another example would be when you want to mutate something at a remote:
(defmethod mutate 'item/update
[{:keys [state ast]} key {:keys [id title]}]
{:remote (assoc ast :params {:data {:id id :title title })})
Here you need to explicitly tell Om to include the data you want to send in the AST. At your remote you then pick apart :data to update the title at the given id
Most of the time you won't use the functions you described in your questions directly. The env available in every method of the parser has the ast in it.
Something I stumbled on, while trying to use Compassus:
Let's say you have a complex union/join query that includes parametric sub-queries. Something like this:
`[({:foo/info
{:foo/header [:foo-id :name]
:foo/details [:id :description :title]}} {:foo-id ~'?foo-id
:foo-desc ~'?foo-desc})]
Now let's say you want to set parameters so on the server you can parse it with om/parser and see those params as 3rd argument of read dispatch. Of course it's possible to write a function that would find all necessary parameters in the query and set the values. That's not easy though, and as I said - imagine your queries can be quite complex.
So what you can do - is to modify ast, ast includes :children :params key. So let's say the actual values for :foo-id and :foo-desc are in the state atom under :route-params key:
(defn set-ast-params [children params]
"traverses given vector of `children' in an AST and sets `params`"
(mapv
(fn [c]
(let [ks (clojure.set/intersection (-> params keys set)
(-> c :params keys set))]
(update-in c [:params] #(merge % (select-keys params (vec ks))))))
children))
(defmethod readf :foo/info
[{:keys [state query ast] :as env} k params]
(let [{:keys [route-params] :as st} #state
ast' (-> ast
(update :children #(set-ast-params % route-params))
om/ast->query
om.next.impl.parser/expr->ast)]
{:value (get st k)
:remote ast'}))
So basically you are:
- grabbing ast
- modifying it with actual values
you think maybe you can send it to server right then. Alas, no! Not yet. Thing is - when you do {:remote ast}, Om takes :query part of the ast, composes ast out of it and then sends it to the server. So you actually need to: turn your modified ast into query and then convert it back to ast again.
Notes:
set-ast-params function in this example would only work for the first level (if you have nested parametrized queries - it won't work),
make it recursive - it's not difficult
there are two different ways to turn ast to query and vice-versa:
(om/ast->query) ;; retrieves query from ast and sets the params based
;; of `:params` key of the ast, BUT. it modifies the query,
;; if you have a join query it takes only the first item in it. e.g. :
[({:foo/foo [:id]
:bar/bar [:id]} {:id ~'?id})]
;; will lose its `:bar` part
(om.next.impl.parser/ast->expr) ;; retrieves query from an ast,
;; but doesn't set query params based on `:params` keys of the ast.
;; there are also
(om/query->ast) ;; and
(om.next.impl.parser/expr->ast)
I'm developing a library in clojure, which - in a way - needs to be stateful. In order to not spending too many word in abstract descriptions, here would be an OOP example of how I'd imagine the API of the library could be used.
mylib = new lib();
state1 = mylib.getState();
mylib.continue();
state2 = mylib.getState();
mylib.continue();
state3 = mylib.getState();
[..]
(whereas obviously state1 != state2 != state3)
Ok, how could this be done in a functional language like Clojure?
One approach, that comes to my mind:
(require '(lib.core :as mylib))
(def state1 (mylib/start-state))
(def state2 (mylib/continue state1))
(def state3 (mylib/continue state2))
[..]
This approach does not allocate the state-keeping to the library-side. The problem I have with this is the following: my state does keep information, that should be public to the API user. But also it keeps information that is important for the generation of the next state, which is however not relevant to the public.
Well, there could be another function (mylib/extract-relevant-data state1) which could postprocess the state in oder for a "clean usage".
I would be really intersted in learning in which ways I could approach this in Clojure.
Method 1: reimplement OOP. I'm treating last-time-called as private data and relevant state as what you want to show to people.
(defn start-state []
(let [last-time-called (atom (new java.util.Date))
relevant-state (atom 1)
private-update (fn [] (swap! last-time-called (fn [_] (new java.util.Date))))
get-state (fn [] (do (private-update)
#relevant-state))
continue (fn [] (do (private-update)
(swap! relevant-state inc)
nil))]
{:get-state get-state :continue continue}))
Demonstration:
stack-prj.hiddenState> (def mylib (start-state))
#'stack-prj.hiddenState/mylib
stack-prj.hiddenState> ((mylib :get-state))
1
stack-prj.hiddenState> ((mylib :continue))
nil
stack-prj.hiddenState> ((mylib :get-state))
2
stack-prj.hiddenState> ((mylib :continue))
nil
Note that get-state and continue have access to last-time-called, if they need it.
Method 2: pure functions on simple data.
(defn new-lib []
{:relevant-state 1
:last-time-called (new java.util.Date)})
(defn get-state [lib]
(lib :relevant-state))
(defn lib-continue [lib]
{:relevant-state (inc (lib :relevant-state))
:last-time-called (new java.util.Date)})
Demonstration:
stack-prj.noHiddenState> (def mylib (new-lib))
#'stack-prj.noHiddenState/mylib
stack-prj.noHiddenState> (get-state mylib)
1
stack-prj.noHiddenState> (def ml2 (lib-continue mylib))
#'stack-prj.noHiddenState/ml2
stack-prj.noHiddenState> (get-state ml2)
2
Note that with method 2, you cannot update the object's private variables when the user accesses the state via get-state. If you need this functionality, then method 1 meets your needs better; if you don't, then method 2 offers more clean, idiomatic, and maintainable code than the first method.
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?
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.
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.