How to parse metadata from Clojure namespace form? - clojure

If I have a namespace form, how do I extract the metadata map from it? For example:
(some-function '(ns ^{:doc "docstring"})) => {:doc "docstring"}

Reader metadata introduced with ^ is attached to the next form to be read in.
The quoted form in your example is malformed as Clojure code (that is, without even considering it being an ns form in particular), because the metadata map has nothing to attach itself to. A fixed version might be (ns ^{:doc "docstring"} foo). Here foo becomes the (mandatory) "name" argument to ns.
Now you can simply extract the symbol foo from the ns form using, say, second and read its metadata map:
(-> '(ns ^{...} foo) second meta)
Note that docstrings for namespaces can be provided in the form of an extra argument to ns:
(ns foo
"foo's docstring"
...)
Also, an additional "attribute map" may be provided as an extra argument (following the docstring if one is given in this form, immediately after the name symbol otherwise). This attribute map will be merged with the metadata of the name symbol and may of course include the key :doc.
So if you want to obtain "the docstring, if any", you'll need to look at the metadata on the name symbol, the form immediately following it (if any; check if it's a string or a map) and possibly one more form (if a docstring is given as the second argument to ns; check if it's a map).

Related

ocamldoc not properly displaying parameter names or description with #param

Simply put, let us say I have the following OCaml file called test.ml:
(**
[Test] is a sample module for showing the problem I am having with # tags with OCamlDoc
*)
(**
[id] is the identity function. For any argument [x], [id x] is [x].
#param x The argument, which will be returned
#return The argument [x]
*)
let id (x: 'a): 'a = x
If I run the command ocamldoc -html -all-params -colorize-code test.ml to get the documentation for the Test module, I get the following result:
As can be seen, for the parameter information, it puts () as the name of the parameter and does not include a description for the parameter for some reason.
I am unsure why the parameter name and description are not properly showing up.
If you write let id x = x the display is correct:
The problem is that ocamldoc will not display #param if you provide a tag that doesn't match a named argument but it's not able to extract a named argument from (id : type).
This is a known bug but sadly nobody touched it so... https://github.com/ocaml/ocaml/issues/8804
As ocamldoc's part-time maintainer, there are very few reasons to still use ocamldoc when odoc is available, even more so when writing new documentation.
Ocamldoc's handling of param tag is far too complex for its own good: ocamldoc tries to peek at the definition of the function and only accepts param tag that matches the argument that it can recognize. Here, it fails on explicit type annotation.

Modyfing higher order functions output in ClojureScript

I want to create helper function that modifies output returned by higher-order function.
Example of higher-order function:
(def use-meta-sub (make-app-fn (make-sub :meta)))
The problem is that when using this in let like this:
(let [meta-sub (use-meta-sub props :get-me-meta-of-this)])
returns map of key-value pairs like this:
{:key-we-almost-never-need-1 nil
:key-we-almost-never-need-2 nil
:key-we-almost-never-need-3 nil
:key-we-almost-never-need-4 nil
:meta-data {:something-useful "foo"
:more-usefull "bar"
:and-so-on "baz"}}
I would like to target only this :meta-data key
I tried this and some other variations:
(defn use-meta-data-sub [props ctrl]
(:meta-data (use-meta-sub props ctrl))) ;; both with get
It does not work because (I think, that returns lazy sequence and I do not know how to actually firs evaluate it and then extract the key from output).
Is this possible without editing 1st higher-order function, and other two it contains?
(I read all documentation on clojure site and clojure-unraveled, either I do not understand them, or they do not cover this)

How can I execute custom tag readers in different order in Clojure EDN

I have a following EDN file
:abc #request/builder/abc {
"def" #request/builder/def {
"someVector" ["sample1", "sample2"]
}
}
I have defined custom tag reader in Clojure, which internally calls java code
(defn custom-readers []
{
#request/builder/def defBuilder
#request/builder/abc abcBuilder
}
)
(defn defBuilder [params]
(.defBuilder (someJavaUtilityClass.) params)
)
(defn abcBuilder [params]
(.abcBuilder (someJavaUtilityClass.) params)
)
When I read EDN using edn/read-string, defBuilder executes first and its value gets passed to abcBuilder.
I want to reverse the order of execution without modifying EDN. I want to modify abcBuilder code such that if java call in abcBuilder returns some value then only execute defBuilder. How can I achieve this.
I tried by modifying code as below
(defn defBuilder [params]
'(.defBuilder (someJavaUtilityClass.) params)
)
(defn abcBuilder [params]
if((.abcBuilder (someJavaUtilityClass.) params)
(eval (get params "def"))
)
)
But this throws error like it "Unable to resolve someJavaUtilityClass and params". Is there a better way to solve this?
I'm afraid that's not possible. That isn't how EDN's tagged literals work. The tag handler is called after reading the form, which includes calling the tag handlers for any tagged literals in the form. In other words, the tag handlers are called inside-out.
If this weren't the case, then what a tag means will depend on where that tagged literal is situated, which is needlessly context dependent.
Check out: https://github.com/edn-format/edn#tagged-elements
Here's the relevant part:
Upon encountering a tag, the reader will first read the next element (which may itself be or comprise other tagged elements), then pass the result to the corresponding handler for further interpretation, and the result of the handler will be the data value yielded by the tag + tagged element, i.e. reading a tag and tagged element yields one value.
Also check out: https://clojure.org/reference/reader#tagged_literals
This is about the Clojure reader. And again, here is the relevant bit:
by invoking the Var #'my.project.foo/bar on the vector [1 2 3]. The data reader function is invoked on the form AFTER it has been read as a normal Clojure data structure by the reader.
Also, symbols can only contain one /, delimiting the namespace and the name. Check out: https://github.com/edn-format/edn#symbols

Meaning of # in clojure

In clojure you can create anonymous functions using #
eg
#(+ % 1)
is a function that takes in a parameter and adds 1 to it.
But we also have to use # for regex
eg
(clojure.string/split "hi, buddy" #",")
Are these two # related?
There are also sets #{}, fully qualified class name constructors #my.klass_or_type_or_record[:a :b :c], instants #inst "yyyy-mm-ddThh:mm:ss.fff+hh:mm" and some others.
They are related in a sence that in these cases # starts a sequence recognisible by clojure reader, which dispatches every such instance to an appropriate reader.There's a guide that expands on this.
I think this convention exists to reduce the number of different syntaxes to just one and thus simplify the reader.
The two uses have no (direct) relationship.
In Clojure, when you see the # symbol, it is a giant clue that you are "talking" to the Clojure Reader, not to the Clojure Compiler. See the full docs on the Reader here: https://clojure.org/reference/reader.
The Reader is responsible for converting plain text from a source file into a collection of data structures. For example, comparing Clojure to Java we have
; Clojure ; Java
"Hello" => new String( "Hello" )
and
[ "Goodbye" "cruel" "world!" ] ; Clojure vector of 3 strings
; Java ArrayList of 3 strings
var msg = new ArrayList<String>();
msg.add( "Goodbye" );
msg.add( "cruel" );
msg.add( "world!" );
Similarly, there are shortcuts that the Reader recognizes even within Clojure source code (before the compiler converts it to Java bytecode), just to save you some typing. These "Reader Macros" get converted from your "short form" source code into "standard Clojure" even before the Clojure compiler gets started. For example:
#my-atom => (deref my-atom) ; not using `#`
#'map => (var map)
#{ 1 2 3 } => (hash-set 1 2 3)
#_(launch-missiles 12.3 45.6) => `` ; i.e. "nothing"
#(+ 1 %) => (fn [x] (+ 1 x))
and so on. As the # or deref operator shows, not all Reader Macros use the # (hash/pound/octothorpe) symbol. Note that, even in the case of a vector literal:
[ "Goodbye" "cruel" "world!" ]
the Reader creates a result as if you had typed:
(vector "Goodbye" "cruel" "world!" )
Are these two # related?
No, they aren't. The # literal is used in different ways. Some of them you've already mentioned: these are an anonymous function and a regex pattern. Here are some more cases:
Prepending an expression with #_ just wipes it from the compiler as it has never been written. For example: #_(/ 0 0) will be ignored on reader level so none of the exception will appear.
Tagging primitives to coerce them to complex types, for example #inst "2019-03-09" will produce an instance of java.util.Date class. There are also #uuid and other built-in tags. You may register your own ones.
Tagging ordinary maps to coerce them to types maps, e.g. #project.models/User {:name "John" :age 42} will produce a map declared as (defrecord User ...).
Other Lisps have proper programmable readers, and consequently read macros. Clojure doesn't really have a programmable reader - users cannot easily add new read macros - but the Clojure system does internally use read macros. The # read macro is the dispatch macro, the character following the # being a key into a further read macro table.
So yes, the # does mean something; but it's so deep and geeky that you do not really need to know this.

Create a keyword from a number

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.