It seems to me that what I want to achieve is trivial; nevertheless it gives me quite some headaches to figure it out on my own.
I have a service implementation written in Clojure which speaks to a Database backend via JPA.
I have to do something like this:
(defn foo []
(let [entitymanager ... query ...]
(do
(.. entitymanager (getTransaction) (begin))
(read-from-db query) ; <-- shall be returned
(.. entitymanager (getTransaction) (commit))
)
)
)
with read-from-db returning a value. I looked into the java.jdbc package and it looks promising but I want to/have to stick to the JPA. Furthermore, my problem seems not to be specific to the JPA per sé.
Is there an idiomatic way to do this kind of wrapping of the return value in Clojure?
Please bear in mind I am a Clojure novice.
Kind regards!
I am not sure to understand your issue. Here is my answer on what I guess is your issue:
(defn foo []
(let [entitymanager ... query ...]
(do
(.. entitymanager (getTransaction) (begin))
(let [db-reply (read-from-db query)] ; <-- shall be returned
(.. entitymanager (getTransaction) (commit))
db-reply)
)
)
)
Correct?
Related
Are there any reasons not to use a wildcard pull?
(defn pull-wild
"Pulls all attributes of a single entity."
[db ent-id]
(d/pull db '[*] ent-id))
It's much more convenient than explicitly stating the attributes.
It depends on which attributes you need to have in your application and if it's data intensive or whether you want to pull lots of entities.
In case you use the client-library, you might want to minimize the data that needs to be send over the wire.
I guess there are lots of other thoughts about that.
But as long as it's fast enough I would pull the wildcard.
fricke
You may also be interested in the entity-map function from Tupelo Datomic. Given an EID (or a Lookup Ref) it will return the full record as a regular Clojure map:
(let [
; Retrieve James' attr-val pairs as a map. An entity can be referenced either by EID or by a
; LookupRef, which is a unique attribute-value pair expressed as a vector.
james-map (td/entity-map (live-db) james-eid) ; lookup by EID
james-map2 (td/entity-map (live-db) [:person/name "James Bond"] ) ; lookup by LookupRef
]
(is (= james-map james-map2
{:person/name "James Bond" :location "London" :weapon/type #{:weapon/wit :weapon/gun} } ))
tl;dr
How can I derive a keyword from a number in ClojureScript:
(keyword 22)
;;=> :22 but in fact returns nil.
Background
In my ClojureScript/Hoplon application I make HTTP requests via cljs-http. Parts of the response I receive look like this:
{:companies
{:22 {:description ... } ; A company.
:64 {:description ... }
... }
{:offers
[{:description ... } ; An offer.
{:description ... }
... ]
Each offer within the vector behind :offers has a :companyId which represents a key in :companies. As soon as I receive the response, I reset! a cell (similar to an atom) query.
Now, I'd like to iterate over each offer and call a function offer-tpl that creates the corresponding HTML. In order to do so, offer-tpl needs the offer itself as well as the related company:
(for [offer (:offers #query)]
(offer-tpl offer (get-in #query [:companies (keyword (:companyId offer))]))))))
Despite the fact that this surely can be done more elegant (suggestions very appreciated), the get-in doesn't work. (:companyId offer) returns a number (e.g. 22) but (keyword (:companyId offer)) returns nil. Calling (keyword (str (:companyId offer))) does the trick, but aren't there any other ways to do this?
(keyword "22") or (keyword (str 22)) returns :22
The reason you are getting :22 is likely because of the keywordize-keys option of a JSON translation. For example:
cljs-http defaults to keywordize-keys for jsonp:
https://github.com/r0man/cljs-http/blob/1fb899d3f9c5728521786432b5f6c36d1d7a1452/src/cljs_http/core.cljs#L115
But you can (and should) in this case pass in a flag to disable keywordization.
Not all keys in JSON are appropriate for Clojure keywordization. For example spaces in a JSON key are valid, but not in Clojure.
Please be aware that numeric keywords are probably incorrect.
https://clojuredocs.org/clojure.core/keyword#example-542692cec026201cdc326d70
It seems like that caveat has been removed from the current Clojure website, so perhaps that means something but I'm not sure what.
http://clojure.org/reference/reader Currently states that
Keywords - Keywords are like symbols, except: They can and must begin
with a colon, e.g. :fred. They cannot contain '.' or name classes.
Like symbols, they can contain a namespace, :person/name A keyword
that begins with two colons is resolved in the current namespace: In
the user namespace, ::rect is read as :user/rect
and that
Symbols begin with a non-numeric character and can contain
alphanumeric.
This definition of a keyword excludes :22 and :with spaces
The keyword function returns a result for invalid input, but this is not an endorsement, it is simply because checking for incorrect input would be a performance overhead in a core part of Clojure.
In short, not all JSON keys translate to keywords, so you should avoid keywordize-keys unless you know the keyspace and/or doing so provides some conveniences.
i am learning clojure. My question is it posible to use (case) inside (-> ).
For example, i want something like this (this code dont work):
(defn eval-xpath [document xpath return-type]
(-> (XPathFactory/newInstance)
.newXPath
(.compile xpath)
(case return-type
:node-list (.evaluate document XPathConstants/NODESET)
:node (.evaluate document XPathConstants/NODE)
:number (.evaluate document XPathConstants/NUMBER)
)
))
Or will be better to use multimethods instead? What is the right 'clojure way for this?
Thank you.
The arrow macro (->) just rewrites its arguments so that the value of the nth form is inserted as the first argument to the n+1th form. What you are writing is equivalent to:
(case
(.compile
(.newXPath (XPathFactory/newInstance))
xpath)
return-type
:node-list (.evaluate document XPathConstants/NODESET)
:node (.evaluate document XPathConstants/NODE)
:number (.evaluate document XPathConstants/NUMBER)
In a general case you can pick one of the three forms to be your tail form ahead of time using let, and then thread that in at the end of the threading macro. Like so:
(defn eval-xpath [document xpath return-type]
(let [evaluator (case return-type
:node-list #(.evaluate % document XPathConstants/NODESET)
:node #(.evaluate % document XPathConstants/NODE)
:number #(.evaluate % document XPathConstants/NUMBER))]
(-> (XPathFactory/newInstance)
.newXPath
(.compile xpath)
(evaluator))))
However what you are really trying to do is map a keyword to a constant on XPathConstants. This can be done with a map. Consider the following:
(defn eval-xpath [document xpath return-type]
(let [constants-mapping {:node-list XPathConstants/NODESET
:node XPathConstants/NODE
:number XPathConstants/NUMBER}]
(-> (XPathFactory/newInstance)
.newXPath
(.compile xpath)
(.evaluate document (constants-mapping return-type)))))
You have a mapping of keywords to constants, so use Clojure's data structures to express that. Additionally the real value of the threading macro is helping you compile the xpath. Don't be afraid to give the data you're using locally scoped names to help you keep track of what you are doing. It also helps you avoid trying to shoehorn things into the threading macro that really don't want to fit.
Check out the following clojure library for working with xpath expressions: https://github.com/kyleburton/clj-xpath
I wonder, how can I print in LISP each new value from loop "for" in new list, which each time creates by calling the function.
I have created the func:
(defun make (id name surname) (list :id id :name name :surname surname) )
Here I created the global variable:
(defvar *db* nil)
And here I defined the func for adding each new value to store it in db:
(defun add (cd) (push cd *db*))
So, I'm able to add each new data to db, like that:
(add (make 0 "Oleg" "Orlov" ) )
To look the content of my db , I can use:
*db*
So, I wonder how to put each new record-list to db using "for" loop, I print values in "for" loop in lisp like this:
(loop for i from 1 to 10 do ( ... ))
If , I use:
(loop for i from 0 to 10 do (add (make i "Oleg" "Orlov") ) )
If you read db using *db* you will see, that all evelen records were added, but after calling the last line, you will get NIL result in return.
Why do I catch NIL result, not T and what does it mean?
Thanks, best regards!
Every form in Lisp evaluates to something.
If a form you type in doesn't return a value, it will evaluate to NIL by default (otherwise, it evaluates to the value(s) it returns). Your loop doesn't actually return a value itself; it just performs 10 assignments (each of the intermediate expressions do return a value, but you don't collect and return them). Therefore, that code will return NIL.
If you haven't done so already, check out chapter 3 of Practical Common Lisp, in which Peter Seibel goes step-by-step through creating a simple database. It might give you some insights into the basics of how Lisp works. The specific question you ask (why forms return NIL by default, and what it means specifically in the context of Common Lisp) is answered in chapter 2 of the same book
As for how you would explicitly cause your loop to emit the list of items it added to *db*, try the following
(loop for i from 1 to 10
for elem = (make i "Oleg" "Orlov")
do (add elem)
collect elem)
I have defined the following code to allow me to set column values in a java.sql.PreparedStatement. Is this code reasonable/idiomatic? How could it be improved?
(use '(clojure.template :only [do-template]))
; (import all java types not in java.lang)
(defprotocol SetPreparedStatement
(set-prepared-statement [this prepared-statement index]))
(do-template [type-name set-name]
(extend-type type-name
SetPreparedStatement
(set-prepared-statement [this prepared-statement index]
(set-name prepared-statement index this)))
BigDecimal .setBigDecimal
Boolean .setBoolean
Byte .setByte
Date .setDate
Double .setDouble
Float .setFloat
Integer .setInt
Long .setLong
Object .setObject
Short .setShort
Time .setTime
Timestamp .setTimestamp)
; Sample use
(set-prepared-statement 42 some-prepared-statement 1)
Your example looks as close to idiomatic Clojure as I can tell :)
It could perhaps benefit from abstracting the type mapping out if you have situations where you will be creating more than one template though if your creating just this one then this looks like excellent clojure to me.