When I go to the source of 'first' this is what I see -
(def
^{:arglists '([coll])
:doc "Returns the first item in the collection. Calls seq on its
argument. If coll is nil, returns nil."
:added "1.0"
:static true}
first (fn ^:static first [coll] (. clojure.lang.RT (first coll))))
So following is the source -
(. clojure.lang.RT (first coll))
What does it mean ? Where is the source of 'first'?
clojure.lang.RT is a java class defined in the clojure source. first
is a static method defined on that class.
Related
I was searching and was not able to find the answer to my question.
I'm reading Clojure For The Brave and True and found the implementation of the map over map values.
(reduce (fn [new-map [key val]]
(assoc new-map key (inc val)))
{}
{:max 30 :min 10})
which works fine, except it throws an error in both REPL and file:
java.lang.Exception: Unable to resolve symbol: new-map in this context
when executed inside a function:
(defn map-map [fn hash]
(reduce (fn [new-map [key val]]
(assoc new-map key (inc val)))
{}
hash))
My own implementation (after reading 3rd chapter) works fine:
(defn map-map [fn hash]
(into {} (map #(conj [(first %)] (fn (second %))) hash)))
The problem is that I've shadowed the fn with an argument. new-map is no longer a parameter in an anonymous function, and Clojure tries to evaluate it as the value in a vector that is passed to function fn.
(defn map-map [func hash]
(reduce (fn [new-map [key val]]
(assoc new-map key (func val)))
{}
hash))
Silly mistake, I'm adding this answer just in case someone will have the same error, and I've already taken the time to create a question.
This was the first step as you can see there is inc in the original code that I didn't yet replace with fn, because of a compiler error even in REPL.
Also, the problem was that my first implementation of map-map didn't use the fn macro so I could use it as a function.
I'm new to Clojure and as a learning exercise I'm trying to write a function that validates the present of keys in a map.
when I try to run the code below I get an error saying
java.lang.UnsupportedOperationException: nth not supported on this type: Keyword
(def record {:name "Foobar"})
(def validations [:name (complement nil?)])
(defn validate
[candidate [key val]]
(val (get candidate key))
(def actual (every? (partial validate record) validations))
(= true actual)
As I understand I'm partial applying the validate function and asserting every validations function on the map - but it doesn't seem to work - so I must be misunderstanding something?
The error is coming from the destructuring that you are using in validate: [key val]. Under the hood, destructuring uses the function nth, and that's what's failing.
Your issue is that you are passing to every? a list of [keyword validation-function]. And every? is iterating over each element of that list and calling the partially applied validate function with it. That means that your validate is called first with the keyword :name and that throws an exception, because you can not extract a [key val] pair out of the keyword :name, causing the exception.
To fix it, you need to make your validations list a list of lists as so:
(def record {:name "Foobar"})
(def validations [[:name (complement nil?)]])
(defn validate
[candidate [key val]]
(val (get candidate key)))
(def actual (every? (partial validate record) validations))
(= true actual)
;; => true
That way, every? is now iterating over each pair of [keyword validation-function], one at a time, and calling validate with that. Since this is a pair, it can be destructured into a [key val] and everything works.
And just so you know, in newer Clojure versions (1.6 and above), there is now a function called some? which is equivalent to (complement nil?).
every? takes a collection as the second argument, and so does your validate function. Since you're passing a vector to every?, validate is being called on the contents of the vector (that is, :name and (complement nil?)). You're also missing a closing paren in the definition of validate. Try the following:
(def record {:name "Foobar"})
(def validations [:name (complement nil?)])
(defn validate
[candidate [key val]]
(val (get candidate key)))
(def actual (every? (partial validate record) [validations]))
(= true actual)
BTW, you could use some? instead of (complement nil?)
I am collecting all user defined functions within a project. As a way of testing membership I use:
(defn get-var-namespace
[qualified-var]
{:pre [(var? qualified-var)]}
(-> qualified-var meta :ns))
(defn project-name
"returns a string representation of the root project name.
this is the same as the directory that the project is located in"
[]
(last (str/split (System/getProperty "user.dir") #"/")))
(defn get-project-namespaces
"return all namespaces defined within the project"
[]
(let [p-name (project-name)]
(filter (fn [x]
(let [n (first (str/split (str (ns-name x)) #"\."))]
(= n p-name))) (all-ns))))
(defn user-defined-var? [project-namespaces var]
(some #{(get-var-namespace var)} project-namespaces))
(defn namespace-deps
"returns a map whose keys are user defined functions and vals are sets of
user defined functions."
[project-namespaces ns]
(let [find-vars-in-expr
(fn [x] (let [nodes (ast/nodes x)
top-level (:var (first nodes))
non-recursive-deps (remove #{top-level}
(filter (partial var-has-user-ns project-namespaces)
(filter some? (map :var nodes))))]
{top-level (set non-recursive-deps)}))]
(dissoc (apply merge-with clojure.set/union
(map find-vars-in-expr (jvm/analyze-ns ns))) nil)))
as an example
clj-smart-test.core> (clojure.pprint/pprint (namespace-deps (get-project-namespaces) *ns*))
{#'clj-smart-test.core/foo
#{#'clj-smart-test.baz/baz1 #'clj-smart-test.core/bar
#'clj-smart-test.foo/foo #'clj-smart-test.core/k},
#'clj-smart-test.core/get-project-namespaces
#{#'clj-smart-test.core/project-name},
#'clj-smart-test.core/project-name #{},
#'clj-smart-test.core/var-has-user-ns
#{#'clj-smart-test.core/get-var-namespace},
#'clj-smart-test.core/bar #{},
#'clj-smart-test.core/f #{},
#'clj-smart-test.core/get-var-namespace #{},
#'clj-smart-test.core/namespace-deps
#{#'clj-smart-test.core/var-has-user-ns},
#'clj-smart-test.core/k #{}}
nil
Are there any corner cases that I need to be aware of? I know that a user could just create an arbitrary namespace in a file so I can't always assume that the directory that a project is created for example lein new my-project, which gives my-project as the root dir. This version does not catch defrecord etc.
(first ["a" "b" "c"])
->
"c"
where I would expect:
(first ["a" "b" "c"])
->
"a"
I think I must have misunderstood something here ,any help appreciated!
Best Regards.
(defn binnd-to-name [name-string to-bind]
(bind-to-name name-string to-bind))
(defmacro bind-to-name [name-string stuff-to-bind]
`(def ~(symbol name-string) ~stuff-to-bind))
(defn bind-services [list-of-services]
(if (empty? list-of-services)
nil
(do
(binnd-to-name (first (first list-of-services)) (last (first list-of-services)))
(bind-services (rest list-of-services)))))
(bind-services [["*my-service*" se.foo.bar.service.ExampleService]])
ExampleService is a Java class on the classpath, which I want to bind to the symbol my-service.
The idea is to loop through a list of name-value pairs and bind each name to the value.
It is not working as expected though.
So somehow in this code something evaluated into "def first last" apparently.
Problem is with your macros not expanding as you expect
(defmacro bind-to-name [name-string stuff-to-bind]
`(def ~(symbol name-string) ~stuff-to-bind))
(defmacro bind-services [services]
`(do
~#(for [s services]
`(bind-to-name ~(first s) ~(second s)))))
(bind-services [["*my-service*" se.foo.bar.service.ExampleService]])
If you try this approach your def symbol sequence will properly expand.
No way!
user=> (doc first)
-------------------------
clojure.core/first
([coll])
Returns the first item in the collection. Calls seq on its
argument. If coll is nil, returns nil.
user=> (first ["a" "b" "c"])
"a"
I am working in clojure with a java class which provides a retrieval API for a domain specific binary file holding a series of records.
The java class is initialized with a file and then provides a .query method which returns an instance of an inner class which has only one method .next, thus not playing nicely with the usual java collections API. Neither the outer nor inner class implements any interface.
The .query method may return null instead of the inner class. The .next method returns a record string or null if no further records are found, it may return null immediately upon the first call.
How do I make this java API work well from within clojure without writing further java classes?
The best I could come up with is:
(defn get-records
[file query-params]
(let [tr (JavaCustomFileReader. file)]
(if-let [inner-iter (.query tr query-params)] ; .query may return null
(loop [it inner-iter
results []]
(if-let [record (.next it)]
(recur it (conj results record))
results))
[])))
This gives me a vector of results to work with the clojure seq abstractions. Are there other ways to expose a seq from the java API, either with lazy-seq or using protocols?
Without dropping to lazy-seq:
(defn record-seq
[q]
(take-while (complement nil?) (repeatedly #(.next q))))
Instead of (complement nil?) you could also just use identity if .next does not return boolean false.
(defn record-seq
[q]
(take-while identity (repeatedly #(.next q))))
I would also restructure a little bit the entry points.
(defn query
[rdr params]
(when-let [q (.query rdr params)]
(record-seq q)))
(defn query-file
[file params]
(with-open [rdr (JavaCustomFileReader. file)]
(doall (query rdr params))))
Seems like a good fit for lazy-seq:
(defn query [file query]
(.query (JavaCustomFileReader. file) query))
(defn record-seq [query]
(when query
(when-let [v (.next query)]
(cons v (lazy-seq (record-seq query))))))
;; usage:
(record-seq (query "filename" "query params"))
Your code is not lazy as it would be if you were using Iterable but you can fill the gap with lazy-seq as follows.
(defn query-seq [q]
(lazy-seq
(when-let [val (.next q)]
(cons val (query-seq q)))))
Maybe you shoul wrap the query method to protect yourself from the first null value as well.