I'm brand new to clojure and the web development stack. I'm trying to use enlive to set values in an HTML template:
(en/deftemplate project-main-page
(en/xml-resource "project-main.html")
[id]
[:#project-name] (en/content (str "Name: " ((get-project id) :name)))
[:#project-desc] (en/content (str "Desc: " ((get-project id) :desc))))
This works fine to set my two HTML elements, but it involves a repeated call to my function get-project. At the moment this just reads from a local map, but eventually it will involve some external storage access, so I'd prefer to just perform it once in this function.
I was thinking of using let:
(en/deftemplate project-main-page
(en/xml-resource "project-main.html")
[id]
(let [project (get-project id)]
[:#project-name] (en/content (str "Name: " (project :name)))
[:#project-desc] (en/content (str "Desc: " (project :desc)))))
But this only affects the description element and ignores the name forms.
What is the best way to bind a local var within deftemplate?
If I have understood what you are trying to achieve; you could also try using the transformation macro provided by enlive.
(defn main-page [{:keys [name desc] :as project}]
(en/transformation
[:#project-name] (en/content (str "Name: " name)
[:#project-desc] (en/content (str "Desc: " desc))))
(en/deftemplate project-main-page
(en/xml-resource "project-main.html")
[id]
(main-page (get-project id)))
The code is untested, but I hope it conveys a different way to do what you need
Enlive's deftemplate macro expects a series of tag/content pairs after the args vector (the args vector is [id] in your example). You can't just stick a let in there because the macro isn't expecting a let form, so when it does its splicing everything gets messed up and results in the behavior you described above.
One way you could fix this would be to write your own deftemplate macro that allows binding definitions using the identifiers in the args vector. Example:
(alt/deftemplate project-main-page
(en/xml-resource "project-main.html")
[id]
[project (get-project id)]
[:#project-name] (en/content (str "Name: " (project :name)))
[:#project-desc] (en/content (str "Desc: " (project :desc))))
The deftemplate macro is a simple wrapper around template, which uses snippet* and this is probably where you'd need to insert your changes:
(defmacro snippet* [nodes args & forms]
`(let [nodes# (map annotate ~nodes)]
(fn ~args
; You could add let bindings here since args are in scope
(doall (flatmap (transformation ~#forms) nodes#)))))
The other option—which might be simpler since you don't have to muck around in the library code—would be to add a level of indirection to your get-project function to cache results. You might try the core.cache library.
Related
Say I have a collection of user-ids i.e. [001 002 003], and then I would have a function that does something and requires the user-id as its first argument.
(defn some-function [user-id name e-mail] (do-something user-id name e-mail))
What I'd like to do is to use this "some-function" to go through the collection of user-ids so that it would just change the user-id argument but the other arguments would remain the same i.e. so that it would return the following:
=>
[(some-function 001 name e-mail) (some-function 002 name e-mail) (some-function 003 name e-mail)]
Any help here? :) Thanks!
You can just use map:
(map #(some-function % name email) user-ids)
If "does something" is side-effecting then you should be using doseq rather than map:
(def user-ids [1 2 3])
(def email "me#my.com")
(def named "me")
(defn some-function [id name email]
(println (str id ", " name ", " email)))
(doseq [user-id user-ids]
(some-function user-id named email))
"Doing something" normally means affecting the world in some way - from printing to the screen to launching rockets into space.
However if you wanted to return a series of functions that can be executed later then map would be fine:
(def fns (map (fn [id]
(fn []
(some-function id named email)))
user-ids))
Here fns is the data structure you wrote out in your question.
To actually execute these 'thunks' you still need to doseq:
(doseq [f fns]
(f))
As a side-note, the kind of function you are talking about, that accepts different arguments at different times, is normally described as a 'higher order function', and it is best to code it that way from the start:
(defn some-function-hof [name email]
(fn [id]
(println (str id ", " name ", " email))))
(def some-fn! (some-function-hof named email))
Originally motivated by the following question: Mapped calls to clojurescript macro
Suppose you want to create many similar functions automatically (i.e. without hand-writing them all). Suppose we have some pre-existing functions and we want wrap them with handlers for a callback of some sort:
(defn do-foo [] (println "I foo'ed"))
(defn do-bar [] (println "I bar'ed"))
(defn do-baz [] (println "I baz'ed"))
(defn manual-on-foo [] (do-foo))
(defn manual-on-bar [] (do-bar))
(defn manual-on-baz [] (do-baz))
(println "Calling manual-on-* functions")
(manual-on-foo)
(manual-on-bar)
(manual-on-baz)
with results:
Calling manual-on-* functions
I foo'ed
I bar'ed
I baz'ed
We want to generate the wrapper functions automatically instead of manually.
You might think you need a macro to create this function, and that is one solution. However, a weakness of macros is that they cannot be passed as arguments to another function such as map. Thus, we could write a macro like:
(generate-fn :foo) ;=> creates `on-foo` w/o hand-writing it
but the following would fail:
(map generate-fn [:foo :bar :baz])
How can we automate the generation of these functions?
Overview
While you can't use map with a macro, you could write a second macro to perform this function. This may, in turn, require writing a third macro, etc, which is the origin of the phrase "Macros All the Way Down" as described in Clojure for the Brave and True and other places.
A similar question was answered here by using Clojure's intern function. Our problem is a little different than that question, since here we use intern in two different ways:
To create a global var like with def or defn
To access the value of a global var using var-get
Function Solution
Using intern allows us to write the following code to automatically generate the on-* functions without using macros:
(defn generate-onstar-f
[event-kw]
(let [
event-str (name event-kw)
do-fn-sym (symbol (str "do-" event-str))
on-fn-sym (symbol (str "on-" event-str))
new-fn (fn on-fn-sym []
(let [the-var (intern 'tst.clj.core do-fn-sym) ; get the var the symbol 'do-fn-sym' points to
the-fn (var-get the-var) ] ; get the fn the var is pointing to
(the-fn))) ]
(intern 'tst.clj.core on-fn-sym new-fn) ; create a var 'on-fn-sym' pointing to 'new-fn'
(println "Created" on-fn-sym)))
(println \newline "*** generating functions ***")
(mapv generate-onstar-f [:foo :bar :baz]) ; creates and interns a functions: my-foo, my-bar, my-baz
(println \newline "Calling automatically generated on-* functions")
(on-foo)
(on-bar)
(on-baz)
with results:
*** generating functions ***
Created on-foo
Created on-bar
Created on-baz
Calling automatically generated on-* functions
I foo'ed
I bar'ed
I baz'ed
So we see that we created the functions on-foo, on-bar & on-baz which, in turn, call the global do-foo, do-bar, & do-baz functions. And we didn't need to use macros!
In Clojure, the var is somewhat of an invisible "middle-man" between a symbol like on-foo and the value it points to (a function in this example). For more information please see the relate post:
When to use a Var instead of a function?
Macro Solution
As mentioned previously, one could use a macro to invoke another macro, side-stepping the problem that macros can't be used with higher-order-functions (HOF) like map. Here we define a new macro run-macro, to replace the map HOF we can't use with generate-onstar-f:
(defmacro generate-onstar-m
[event-kw]
(let [event-str (name event-kw)
do-fn-sym (symbol (str "do-" event-str))
on-fn-sym (symbol (str "on-" event-str "-m"))]
(println "Creating" on-fn-sym)
`(defn ~on-fn-sym []
(~do-fn-sym))))
(println \newline "Using Macro")
(generate-onstar-m :foo)
(on-foo-m)
(defmacro run-macro
"Run the specified macro once for each arg"
[root-macro args]
`(do
~#(forv [item args]
`(~root-macro ~item))))
(println \newline "Generating on-*-m functions using `run-macro`")
(run-macro generate-onstar-m [:foo :bar :baz])
(on-foo-m)
(on-bar-m)
(on-baz-m)
with results:
Using Macro
Creating on-foo-m
I foo'ed
Generating on-*-m functions using `run-macro`
Creating on-foo-m
Creating on-bar-m
Creating on-baz-m
I foo'ed
I bar'ed
I baz'ed
While I may incorrectly interpret the concept of homoiconicity, I've understood it as 'code being data'.
So, I can write code like this:
(def subject "world")
(def helo '(str "Hello " subject))
At this point, helo is only data, but can be executed as code like this:
(eval helo)
which returns "Hello world".
I can also continue to treat helo as data:
(first helo)
(count helo)
which returns respectively str and 3.
So far so good. However, as soon as I wrap the code in a function, I seem to lose the ability to treat code as data:
(defn helofn [subject]
(str "Hello " subject))
How do I decompose helofn? It seems that I can't treat it as data; if I do this:
(count helofn)
I get an exception:
java.lang.UnsupportedOperationException: count not supported on this type: user$helofn
Is there another way to decompose helofn, or am I just expecting too much from homoiconicity?
The helofn definition is data, but you're letting it be evaluated (just as you explicitly evaluated the helo list). If you treated the definition in the same way as helo, then it will remain data, and amenable to whatever transformations you want to apply:
(def helofndata '(defn helofn [subject]
(str "Hello " subject))
=> (second helofndata)
helofn
=> (eval helofndata)
#'user/helofn
defn is just a macro:
(macroexpand '(defn helofn [subject]
(str "Hello " subject)))
(def helofn (clojure.core/fn ([subject] (str "Hello " subject))))
If you define helofn the way you defined helo, you'll be able to treat it as data:
(def helofn '(fn [subject]
(str "Hello " subject)))
Now you can eval and call this function:
((eval helofn) "world")
and to treat it as a data:
(count helofn)
But, when you use defn macro you associates helofn variable with compiled function and not with it's code.
It's not just functions. Let's say you defined hello with the following code:
(def helo (str "Hello " subject))
Now hello is associated with "Hello world" string and not with (str "Hello " subject) code. So, now there is no way to get the code this string was built with.
N.B. If you want to treat clojure code as data you should look into its macros. Any code passed to a macro is treated as data and any data returned by a macro is treated as code.
Homoiconicity is a very powerful concept and I don't think you are expecting too much from it.
defn is actually a macro that uses the def special form to define a function, so:
(defn sq [x]
(* x x))
Is actually equivalent to:
(def sq (fn ([x] (* x x))))
So defn here is receiving the args sq [x] (* x x), then builds the list (def sq (fn ([x] (* x x)))), returns it as the result of the macro and is then eval'ed. This is all done through the manipulation of lists, maps, vectors, symbols, etc., by the defn macro.
The fact that in Clojure you can't get the original list of symbols from which you defined a function, has to do with the fact that in Clojure all code is compiled. This is why evaluating (fn [x] 1) in the REPL returns something like #<user$eval809$fn__810 user$eval809$fn__810#10287d>
. But still, as mentioned in a previous answer, the code that is evaluated is data.
Maybe I'm going too far with this, but if you wanted to have for each function you define, the data from which it was created, you could add it to its metadata by creating your own custom macro.
Here's a naive implementation for such a macro:
(defmacro defn* [x & body ]
(let [form `'~&form
x (vary-meta x assoc :form form)]
`(defn ~x ~#body)))
;=> #'user/defn*
(defn* sq [x]
(* x x))
;=> #'user/sq
(:form (meta #'sq))
;=> (defn* sq [x] (* x x))
&form is an implicit argument (together with &env) that contains the whole (unevaluated) form with which the macro was called (i.e. the data that is evaluated by the compiler).
Hope this helps and it doesn't bring more confusion.
It looks like no based on
get a clojure function's code
and
Can you get the "code as data" of a loaded function in Clojure?
Basically you can get the source from a function defined in a .clj file but there's no reliable way to retrieve the data structures that built a function from the function alone.
EDIT: Also I think you are expecting too much from homoiconicity. The code itself is data yes but it's fairly standard to not be able to retrieve the original source code based on the artifact emitted by that code. Like when I have 2 I have no way of knowing that it was produced by (+ 1 1) or (- 4 2) in the same way a function is a piece of data created by calling fn over some other data structures that get interpreted as code.
I am trying to better understand listing 13.3 in The Joy of Clojure. It is a macro that generates other macros (much like how primitive array functions are implemented in Clojure 1.4).
I want to write a macro that, when run, simply prints the suffix of the generated macro. i.e.
user=> (nested-macro joe)
user=> (nested-macro-named-joe)
hello from joe
nil
I am having trouble making this work.
Here is what I've tried:
Attempt 1
(defmacro nested-macro [name]
`(defmacro ~(symbol (str "nested-macro-named-" name))
[]
`(println "hello from " ~name)))
Output:
hello from #<core$name clojure.core$name#502c06b2>
Attempt 2
(defmacro nested-macro [name]
(let [local-name name]
`(defmacro ~(symbol (str "my-custom-macro-named-" ~local-name))
[]
`(println "hello from " ~local-name))))
Error
IllegalStateException Attempting to call unbound fn: #'clojure.core/unquote clojure.lang.Var$Unbound.throwArity (Var.java:43)
Attempt 3:
(defmacro nested-macro [name]
(let [local-name name]
`(defmacro ~(symbol (str "nested-macro-named-" name))
[]
`(println "hello from " ~(symbol local-name)))))
Compiler Error:
CompilerException java.lang.RuntimeException: No such var: joy.dsl/local-name
Just for the heck of it, I've also tried adding # to the local variables, with similar results as above but with "auto" names, such as local-name__1127__auto__ I don't see that being as part of the solution, however.
How can I make this work?
To know what is going wrong with macros I always use macroexpand-1.
From your first example:
(macroexpand-1 '(nested-macro joe))
Results in:
(clojure.core/defmacro nested-macro-named-joe []
(clojure.core/seq
(clojure.core/concat
(clojure.core/list (quote clojure.core/println))
(clojure.core/list "hello from ")
(clojure.core/list clojure.core/name))))
If you look at the last param, it shows that you are using clojure.core/name, which probably is not what you want, as you actually want the parameter named "name".
To fix it, just add another unquote to the name param, but as the name param is actually a symbol, what you really want is to get the name of it as in:
(defmacro nested-macro [the-name]
`(defmacro ~(symbol (str "nested-macro-named-" the-name))
[]
`(println "hello from " ~~(name the-name))))
I have a function that begins like this:
(defn data-one [suser]
(def suser-first-name
(select db/firstNames
(fields :firstname)
(where {:username suser})))
(def suser-middle-name
(select db/middleNames
(fields :middlename)
(where {:username suser})))
(def suser-last-name
(select db/middleNames
(fields :lastname)
(where {:username suser})))
;; And it just continues on and on...
)
Of course, I don't like this at all. I have this pattern repeating in many areas in my code-base and I'd like to generalize this.
So, I came up with the following to start:
(def data-input {:one '[suser-first-name db/firstNames :firstname]
'[suser-middle-name db/middleNames :middlename]
'[suser-last-name db/lastNames :lastname]})
(defpartial data-build [data-item suser]
;; data-item takes the arg :one in this case
`(def (data-input data-item)
(select (data-input data-item)
(fields (data-input data-item))
(where {:username suser}))))
There's really a few questions here:
-- How can I deconstruct the data-input so that it creates x functions when x is unknown, ie. that the values of :one is unknown, and that the quantities of keys in data-input is unknown.
-- I'm thinking that this is a time to create a macro, but I've never built one before, so I am hesitant on the idea.
And to give a little context, the functions must return values to be deconstructed, but I think once I get this piece solved, generalizing all of this will be doable:
(defpage "/page-one" []
(let [suser (sesh/get :username)]
(data-one suser)
[:p "Firat Name: "
[:i (let [[{fname :firstname}] suser-first-name]
(format "%s" fname))]
[:p "Middle Name: "
[:i (let [[{mname :emptype}] suser-middle-name]
(format "%s" mname))]
[:p "Last Name: "
[:i (let [[{lname :months}] suser-last-name]
(format "%s" lname))]]))
Some suggestions:
def inside a function is really nasty - you are altering the global environment, and it can cause all kinds of issues with concurrency. I would suggest storing the results in a map instead.
You don't need a macro here - all of the data fetches can be done relatively easily within a function
I would therefore suggest something like:
(def data-input [[:suser-first-name db/firstNames :firstname]
[:suser-middle-name db/middleNames :middlename]
[:suser-last-name db/lastNames :lastname]])
(def data-build [data-input suser]
(loop [output {}
items (seq data-input)]
(if items
(recur
(let [[kw db fieldname] (first items)]
(assoc output kw (select db (fields fieldname) (where {:username suser}))))
(next items))
output)))
Not tested as I don't have your database setup - but hopefully that gives you an idea of how to do this without either macros or mutable globals!
Nice question. First of all here's the macro that you asked for:
(defmacro defquery [fname table fields ]
(let [arg-name (symbol 'user-name)
fname (symbol fname)]
`(defn ~fname [~arg-name]
(print ~arg-name (str ~# fields)))))
You can call it like that:
(defquery suser-first-name db/firstNames [:firstname])
or if you prefer to keep all your configurations in a map, then it will accept string as the first argument instead of a symbol:
(defquery "suser-first-name" db/firstNames [:firstname])
Now, if you don't mind me recommending another solution, I would probably chose to use a single function closed around configuration. Something like that:
(defn make-reader [query-configurations]
(fn [query-type user-name]
(let [{table :table field-names :fields}
(get query-configurations query-type)]
(select table
(apply fields field-names)
(where {:username suser})))))
(def data-input {:firstname {:table db/firstNames :fields :firstname}
:middlename {:table db/middleNames :fields :middlename}
:lastname {:table db/lastNames :fields :lastname}})
(def query-function (make-reader data-input))
;; Example of executing a query
(query-function :firstname "tom")
By the way there's another way to use Korma:
;; This creates a template select from the table
(def table-select (select* db/firstNames))
;; This creates new select query for a specific field
(def first-name-select (fields table-select :firstname))
;; Creating yet another query that filters results by :username
(defn mkselect-for-user [suser query]
(where query {:username suser}))
;; Running the query for username "tom"
;; I fully specified exec function name only to show where it comes from.
(korma.core/exec (mkselect-for-user "tom" first-name-select))
For more information I highly recommend looking at Korma sources.