Recognize namespaced symbol in a Clojure macro - clojure

I am working on a macro, either which does some code transformations, defined in the file epicea.antivalue.core. Among other things, it should recognize the use of the symbol make as if that symbol was bound in the namespace epicea.antivalue.core. But ideally, the symbol make should be unbound. My code is available here: https://github.com/jonasseglare/epicea-antivalue/tree/stackoverlow-question
To identify the symbol make, I here is some code from epicea.antivalue.core:
(def make ::make)
(defn evals-to-keyword [kwd]
(fn [x]
(try
(and (symbol? x)
(= kwd (eval x)))
(catch Throwable e
false))))
;; Function to test if we are referring to a symbol 'make' in the namespace of this file.
(def make-sym? (evals-to-keyword ::make))
but unfortunately, I need the line
(def make ::make)
in order for it to work.
My problem is reflected by the unit testing code in
epicea.antivalue.core-test.clj
which currently fails:
(ns epicea.antivalue.core-test
(:import [epicea.antivalue AntivalueException])
(:require [clojure.test :refer :all]
[epicea.tag.core :as tag]
[epicea.antivalue.core :as av]))
(deftest contextual-make
;; The call to 'av/make' should only work inside the 'av/either' macro
;; These tests PASS
(is (= 4 (av/either (av/make false 9 3) 4)))
(is (= 9 (av/either (av/make true 9 3) 4)))
;; This test PASSES, as we would expect.
(is (try
(eval '(println epicea.antivalue.core/some-unbound-symbol))
false
(catch Throwable e
true)))
;; But outside the 'av/either' macro, 'av/make' should be unbound.
;; This test FAILS, but I don't want it to fail. How can we achieve that?
(is (try
(eval '(println epicea.antivalue.core/make))
false
(catch Throwable e
true))))
So my question is: How can I implement the function make-sym? without having the the symbol make defined in the epicea.antivalue.core namespace? In other words, how can I make the unit test pass, by only editing the source file epicea.antivalue.core?

Related

How do you use an existing vector of predicates with :post conditions in Clojure?

Given that :post takes a form that gets evaluated later (e.g. {:post [(= 10 %)]}). How could one dynamically pass a 'pre-made' vector of functions to :post?
For example:
(def my-post-validator
[prediate1 predicate2 predicate3])
(defn foo [x]
{:post my-post-validator}
x)
this throws a syntax error
Don't know how to create ISeq from: clojure.lang.Symbol
With my fuzzy understanding, it's because defn is a macro, and the thing that allows the % syntax in :post is that it's quoted internally..?
I thought maybe I then use a macro to pass a 'literal' of what I wanted evaluated
(defmacro my-post-cond [spec]
'[(assert spec %) (predicate2 %) (predicate n)])
example:
(defn foo [x]
{:post (my-post-cond :what/ever)}
x)
However, this attempt gives the error:
Can't take value of a macro
Is there a way to pass a vector of things to :post rather than having to define it inline?
You can't pass a vector of predefined predicates, but you can combine multiple predicates under a single name and use that name in :post:
(defn my-post-cond [spec val]
(and
;; Not sure if this is exactly what you want,
;; given that `val` becomes an assert message.
(assert spec val)
(predicate2 val)
;; You used `n` - I assume it was supposed to be `%`.
(predicate val)))
(defn foo [x]
{:post [(my-post-cond :what/ever %)]}
x)
I started off as a fan of pre- and post-conditions, but I've changed over the years.
For simple things, I prefer to use Plumatic Schema to not only test inputs & outputs, but to document them as well.
For more complicated tests & verifications, I just put in an explicit assert or similar. I also wrote a helper function in the Tupelo library to reduce repetition, etc when debugging or verifying return values:
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(defn oddly
"Transforms its input. Throws if result is not odd"
[x]
(let [answer (-> x (* 3) (+ 2))]
(with-result answer
(newline)
(println :given x)
(assert (odd? answer))
(println :returning answer))))
(dotest
(is= 5 (oddly 1))
(throws? (oddly 2)))
with result
------------------------------------
Clojure 1.10.3 Java 11.0.11
------------------------------------
Testing tst.demo.core
:given 1
:returning 5
:given 2
Ran 2 tests containing 2 assertions.
0 failures, 0 errors.
Passed all tests
So with either the println or assert, the returned value is easy to see. If it fails the assert, an Exception is thrown as normal.

Getting a function's name in its body or :test body

In clojure, can one idiomatically obtain a function's name inside of its body, hopefully accomplishing so without introducing a new wrapper for the function's definition? can one also access the function's name inside of the body of the function's :test attribute as well?
For motivation, this can be helpful for certain logging situations, as well as for keeping the body of :test oblivious to changes to the name of the function which it is supplied for.
A short elucidation of the closest that meta gets follows; there's no this notion to supply to meta, as far as I know, in clojure.
(defn a [] (:name (meta (var a))))
Obviously it is easy to accomplish with a wrapper macro.
Edit: luckily no one so far mentioned lambda combinators.
There are 2 ways to approach your question. However, I suspect that to fully automate what you want to do, you would need to define your own custom defn replacement/wrapper.
The first thing to realize is that all functions are anonymous. When we type:
(defn hello [] (println "hi"))
we are really typing:
(def hello (fn [] (println "hi"))
we are creating a symbol hello that points to an anonymous var which in turn points to an anonymous function. However, we can give the function an "internal name" like so:
(def hello (fn fn-hello [] (println "hi")))
So now we can access the function from the outside via hello or from the inside using either hello of fn-hello symbols (please don't ever use hello in both locations or you create a lot of confusion...even though it is legal).
I frequently use the fn-hello method in (otherwise) anonymous functions since any exceptions thrown will include the fn-hello symbol which makes tracking down the source of the problem much easier (the line number of the error is often missing from the stack trace). For example when using Instaparse we need a map of anonymous transform functions like:
{
:identifier fn-identifier
:string fn-string
:integer (fn fn-integer [arg] [:integer (java.lang.Integer. arg)])
:boolean (fn fn-boolean [arg] [:boolean (java.lang.Boolean. arg)])
:namespace (fn fn-namespace [arg] [:namespace arg])
:prefix (fn fn-prefix [arg] [:prefix arg])
:organization (fn fn-organization [arg] [:organization arg])
:contact (fn fn-contact [arg] [:contact arg])
:description (fn fn-description [arg] [:description arg])
:presence (fn fn-presence [arg] [:presence arg])
:revision (fn fn-revision [& args] (prepend :revision args))
:iso-date (fn fn-iso-date [& args] [:iso-date (str/join args)])
:reference (fn fn-reference [arg] [:reference arg])
:identity (fn fn-identity [& args] (prepend :identity args))
:typedef (fn fn-typedef [& args] (prepend :typedef args))
:container (fn fn-container [& args] (prepend :container args))
:rpc (fn fn-rpc [& args] (prepend :rpc args))
:input (fn fn-input [& args] (prepend :input args))
...<snip>...
}
and giving each function the "internal name" makes debugging much, much easier. Perhaps this would be unnecessary if Clojure had better error messages, but that is a longstanding (& so far unfullfilled) wish.
You can find more details here: https://clojure.org/reference/special_forms#fn
If you read closely, it claims that (defn foo [x] ...) expands into
(def foo (fn foo [x] ...))
although you may need to experiment to see if this has already solved the use-case you are seeking. It works either way as seen in this example where we explicitly avoid the inner fn-fact name:
(def fact (fn [x] ; fn-fact omitted here
(if (zero? x)
1
(* x (fact (dec x))))))
(fact 4) => 24
This version also works:
(def fact (fn fn-fact [x]
(if (zero? x)
1
(* x (fn-fact (dec x))))))
(fact 4) => 24
(fn-fact 4) => Unable to resolve symbol: fn-fact
So we see that the "internal name" fn-fact is hidden inside the function and is invisible from the outside.
A 2nd approach, if using a macro, is to use the &form global data to access the line number from the source code. In the Tupelo library this technique is used to improve error messages for the
(defmacro dotest [& body] ; #todo README & tests
(let [test-name-sym (symbol (str "test-line-" (:line (meta &form))))]
`(clojure.test/deftest ~test-name-sym ~#body)))
This convenience macro allows the use of unit tests like:
(dotest
(is (= 3 (inc 2))))
which evalutes to
(deftest test-line-123 ; assuming this is on line 123 in source file
(is (= 3 (inc 2))))
instead of manually typing
(deftest t-addition
(is (= 3 (inc 2))))
You can access (:line (meta &form)) and other information in any macro which can make your error messages and/or Exceptions much more informative to the poor reader trying to debug a problem.
Besides the above macro wrapper example, another (more involved) example of the same technique can be seen in the Plumatic Schema library, where they wrap clojure.core/defn with an extended version.
You may also wish to view this question for clarification on how Clojure uses the "anonymous" var as an intermediary between a symbol and a function: When to use a Var instead of a function?

compiling snippets of clojure(script) into javascript

Where in the clojurescript library can I access a function to compile snippets of clojure into js?
I need this to run in the clojure (not clojurescript) repl:
(->js '(fn [x y] (+ x y)))
=> "function(x,y){return x+y}"
Snippet compilation from Clojure REPL
(require '[cljs.analyzer.api :refer [analyze empty-env]])
(require '[cljs.compiler.api :refer [emit]])
(let [ast (analyze (empty-env) '(defn plus [a b] (+ a b)))]
(emit ast))
;; result
"cljs.user.plus = (function cljs$user$plus(a,b){\nreturn (a + b);\n});\n"
Snippet compilation from ClojureScript REPL:
(require '[cljs.js :refer [empty-state compile-str]])
(compile-str (empty-state) "(defn add [x y] (+ x y))" #(println (:value %)))
;; Output (manually formatted for easier reading)
cljs.user.add = (function cljs$user$add(x,y){
return (x + y);
});
compile-str takes a callback as the last argument. It will be called with a map either with a key :value containing result JS as a string or :error with the compilation error.
In both cases org.clojure/tools.reader is needed on your classpath.
there is a lightweight alternative: https://github.com/kriyative/clojurejs which creates the right output asked by the question.
Examples can be seen here: https://github.com/kriyative/clojurejs/wiki/Examples

Trouble referring to defrecord symbols in Clojure unit test?

Consider the following leiningen project core.clj file
(ns records.core)
(defn hello [] "hello")
(defprotocol my-sequence
(add [seqq item]))
(defrecord my-vector [coll]
my-sequence
(add [_ item] (conj coll item)))
I can compile this and test it in the REPL as follows:
records.core> (hello)
"hello"
records.core> (add (my-vector. []) 42)
[42]
But when I transcribe this into the leiningen unit-test file as follows:
(ns records.core-test
(:require [clojure.test :refer :all]
[records.core :refer :all]))
(deftest a-test
(testing "adding to a my-vector"
(is (= (hello) "hello"))
#_(is (= [42] (add (my-vector. []) 42)))))
The first test succeeds, showing that symbol hello is correctly moved into the records.core-test namespace, but the test of my-vector throws a compiler error (remove the #_ on the second line above):
clojure.lang.Compiler$CompilerException: java.lang.IllegalArgumentException:
Unable to resolve classname: my-vector, compiling:
(.../records/test/records/core_test.clj:8:22)
This does not seem to be a duplicate of this SO question because I am using require and refer, as the answer to that question suggested.
EDIT: the following also do not help
(add (records.core/my-vector. []) 42)
(add (#'records.core/my-vector. []) 42)
(add (##'records.core/my-vector. []) 42)
Unless my defrecord skills become rusty, you should first require the namespace and then import the defrecord.
(:require records.core)
(:import [records.core my-vector])

get a clojure function's code

Is there a way in clojure to get a function's code after the function has been loaded?
Ie. without doing something like [untested]
(defmacro blat [x] `(do (def code ~(quote (mexpand-all x)))
~x)))
(blat (defn func [abc] (...)))
You can get the source of a symbol using the clojure.repl/source function. However, this only works if the var for which the symbol resolves to is in a .clj file on the classpath. You can't, for example, do this:
user=> (defn foo [x] x)
#'user/foo
user=> (require 'clojure.repl)
nil
user=> (clojure.repl/source foo)
Source not found
nil