Tracing functions when the arguments are big maps - clojure

When I trace a function, if one of the arguments is a nested map with a lot of elements, the trace is filled with clutter. Here's a typical example:
TRACE t36705: (get-value {:nodeclass :simple, :nodeid :simple25, :dock {:constan
t-dock {:name :constant-dock, :value 22, :dockclass {:name :constant-dock, :link
-policy {:lp-committed? #object[fargish.links$fn__5756 0x407956a5 "fargish.links
$fn__5756#407956a5"], :lp-reciprocate-no-commitment #object[clojure.core$constan
tly$fn__4614 0x69497a36 "clojure.core$constantly$fn__4614#69497a36"], :lp-recipr
ocate-commitment #object[clojure.core$constantly$fn__4614 0x30ee413a "clojure.co
re$constantly$fn__4614#30ee413a"], :lp-can-boost-to #object[fargish.links$fn__57
58 0x5df17e60 "fargish.links$fn__5758#5df17e60"], :lp-official-partners #object[
fargish.links$fn__5760 0x3df2f4ab "fargish.links$fn__5760#3df2f4ab"], :lp-normal
ize-after-add #object[clojure.core$constantly$fn__4614 0x386cc1c4 "clojure.core$
constantly$fn__4614#386cc1c4"], :lp-reduce-to-uncommitted #object[fargish.links$
fn__5765 0x7bd4f212 "fargish.links$fn__5765#7bd4f212"], :lp-committed-to #object
[fargish.links$fn__5767 0x5c3cc103 "fargish.links$fn__5767#5c3cc103"], :lp-boost
#object[fargish.links$fn__5771 0x423e35f0 "fargish.links$fn__5771#423e35f0"]},
:maker #object[fargish.spec_test$eval36501$__GT_Dock_constant_dock__36515 0x19cc
229b "fargish.spec_test$eval36501$__GT_Dock_constant_dock__36515#19cc229b"]}}, :
function-dock {:name :function-dock, :value #fargish.spec.Vfunc{:args (constant-
dock), :f #object[fargish.spec_test$fn__36544 0x135647d3 "fargish.spec_test$fn__
36544#135647d3"]}, :dockclass {:name :function-dock, :link-policy {:lp-committed
? #object[fargish.links$fn__5756 0x407956a5 "fargish.links$fn__5756#407956a5"],
:lp-reciprocate-no-commitment #object[clojure.core$constantly$fn__4614 0x69497a3
6 "clojure.core$constantly$fn__4614#69497a36"], :lp-reciprocate-commitment #obje
ct[clojure.core$constantly$fn__4614 0x30ee413a "clojure.core$constantly$fn__4614
#30ee413a"], :lp-can-boost-to #object[fargish.links$fn__5758 0x5df17e60 "fargish
.links$fn__5758#5df17e60"], :lp-official-partners #object[fargish.links$fn__5760
0x3df2f4ab "fargish.links$fn__5760#3df2f4ab"], :lp-normalize-after-add #object[
clojure.core$constantly$fn__4614 0x386cc1c4 "clojure.core$constantly$fn__4614#38
6cc1c4"], :lp-reduce-to-uncommitted #object[fargish.links$fn__5765 0x7bd4f212 "f
argish.links$fn__5765#7bd4f212"], :lp-committed-to #object[fargish.links$fn__576
7 0x5c3cc103 "fargish.links$fn__5767#5c3cc103"], :lp-boost #object[fargish.links
$fn__5771 0x423e35f0 "fargish.links$fn__5771#423e35f0"]}, :maker #object[fargish
.spec_test$eval36523$__GT_Dock_function_dock__36537 0x34584446 "fargish.spec_tes
t$eval36523$__GT_Dock_function_dock__36537#34584446"]}}}} constant-dock)
TRACE t36705: => nil
What's a technique for setting things up so these traces print out without so much clutter? I don't expect traces to be super-easy to read, but there has to be a better way than this.

Unfortunately, clojure.tools.trace doesn't allow customizing the output of trace logs. I wasn't sure if you could modify your source code between runs or you needed a mechanism to plugin into a running system where you cannot modify function implementation.
From your comment it seems you can modify a function you want to trace. Based on your requirement to log only a subset of function's arguments content I would switch to ordinary logging (either with println or with some logging framework) and add explicit logging expressions to log only relevant data.
For example:
(defn some-function
[a b]
(println "some-function" {:a (select-keys a [:x :y]) :b (select-keys b [:x :z])})
(comment your function body))
It's not that easy as changing defn to deftrace but can be customized any way you want.

Related

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.

Polymorphic Schemas in Clojure

I want to create polymorphic schemas/types, and I'm curious of best practices. The following 2 examples allow me to create a Frequency schema which can repeat an event monthly by day of month, or monthly by day of week (eg, every 15th, or every first monday, respectively).
The first one uses the experimental abstract map to accomplish this, and its syntax of it is awkward (IMO). Plus being in the experimental package concerns me a bit.
The second one uses s/conditional, and this suffers from not being able to easily coerce the value of type from a string to keyword, which is useful when dealing with a REST API, or JSON. (whereas s/eq is great for this).
In the general case, is one of these, or some third option, the best practice for conveying: Type A is one of Types #{B C D ...}?
Two options:
;;OPTION 1
​
(s/defschema Frequency (field (abstract-map/abstract-map-schema
:type {})
{}))
(abstract-map/extend-schema MonthlyByDOM Frequency
[:monthly-by-dom]
{:days #{MonthDay}})
(abstract-map/extend-schema MonthlyByDOW Frequency
[:monthly-by-dow]
{:days #{WeekDay}
:weeks #{(s/enum 1 2 3 4 5)}})
;;OPTION 2
​
(s/defschema MonthlyByDOM "monthly by day of month, eg every 13th and 21st day" {:type (s/eq :monthly-by-dom)
:days #{MonthDay}})
(s/defschema MonthlyByDOW "monthly by day of week, eg first, and third friday" {:type (s/eq :monthly-by-dow)
:days #{WeekDay}
:weeks #{(s/enum 1 2 3 4 5)}})
(s/defschema Frequency (field (s/conditional #(= (s/eq :monthly-by-dom) (do (prn %) (:type %))) MonthlyByDOM
#(= :monthly-by-dow (:type %)) MonthlyByDOW)
{:default {:type :monthly-by-dom
:days #{1 11 21}}}))
Similar questions that don't quite help:
https://groups.google.com/forum/#!topic/prismatic-plumbing/lMvazYXRAQQ
Polymorphic schema validation in Clojure
Validating multiple polymorphic values using Prismatic Schema

What is the difference between the user-specified transaction temp-id and the one returned from a transaction, in Datomic?

I have the following clojure function which transacts to a Datomic database:
(defn demo-tran [term description]
(d/transact conn
[{:db/id (d/tempid :db.part/utility -10034)
:utility.tag/uuid (d/squuid)
:utility.tag/term term
:utility.tag/description description}]))
I then run this in the repl:
(demo-tran "Moo" "A bovine beast")
This succeeds and I am given back a 'transaction map':
{:db-before datomic.db.Db,
#f4c9aa60 :db-after,
datomic.db.Db #908ec69f,
:tx-data [#datom[13194139534424 50 #inst"2016-04-01T09:16:50.945-00:00" 13194139534424 true]
#datom[668503069688921 153 #uuid"56fe3c82-8dbd-4a0d-9f62-27b570cbb14c" 13194139534424 true]
#datom[668503069688921 154 "Moo" 13194139534424 true]
#datom[668503069688921 155 "A bovine beast" 13194139534424 true]],
:tempids {-9222699135738586930 668503069688921}}
I have specified the tempid for this transaction as '-10034' so I would expect to find that negative number in the :tempids map. Instead I find -9222699135738586930. This is confusing. What is going on here?
I was hoping to be able to have the demo-tran function return the new EntityID but (other than guessing the position in the :tempids map) there is no way, given my inputs, to get to this value.
As one commenter mentions (via link), you need to use resolve-tempid, as documented here and demonstrated in the day of datomic project here.
In your case this would be something like:
(let [my-tempid (d/tempid :db.part/utility -100034)
tx-result #(d/transact conn [{:db/id my-tempid
:your "transaction"}])
db-after (:db-after tx-result)
tempids (:tempids tx-result)]
(d/resolve-tempid db-after tempids my-tempid))