I have a misbehaving piece of code; when I name a record MethodInfo, it no longer overrides the .toString method correctly.
(defrecord MethodInfo [^clojure.lang.ISeq x ^clojure.lang.ISeq y]
java.lang.Object
(toString [x]
(str (:x x))))
Running a simple test shows how this fails,
=> (.toString (new MethodInfo [1 2] [3]))
"sketch.compiler.main.sklojure1.MethodInfo#10e0d118"
whereas renaming the record to A shows the code behaving correctly,
=> (.toString (new A [1 2] [3]))
"[1 2]"
what am I doing wrong??
Your record works fine for me. I would recommend restarting the REPL as there might be some old code hanging around. Note also that you have direct access to fields in the record, so you can write
(defrecord MethodInfo [x y]
Object
(toString [_] (str x)))
instead of
(defrecord MethodInfo [x y]
Object
(toString [this] (str (:x this))))
Related
When I attach some metadata to a function and then call it I am not able to access those metadata within that function
(let [I (fn I [x] (println I) (println (meta I)))]
(let [f (with-meta I {:rr 5})]
(println I)
(println f)
(f I)))
I see that the self reference from within the function is not the function instance actually invoked and thus no metadata is available through that self reference. I need the self reference to give me the function instance actually invoked to access those metadata
I think that the problem is that your conflating the value of the function and the identity of the function together. It's a thing many other languages do so it's natural when you're learning Clojure. In your example, I has a reference to itself, and looks up the metadata from that reference, which returns nil. You then create f which is the same as I, but with some metadata. So when you run f it looks up the metadata on I and returns nil. Defining f doesn't change I at all, it just creates a new thing in terms of the old thing. If you want to change something you need to introduce a reference type that you can change. There are several of these, but usually to store functions you'd use a Var (see here for reference)
(defn i [] (meta i))
(i) ;;=> nil
(alter-var-root #'i with-meta {:rr 5})
(i) ;;=> {:rr 5}
Here we define a function in the current namespace called i which just returns it's own metadata. We call it to get nil. Then we alter the global reference with some new metadata, and call it again.
If you wanted a more lexically scoped example, you could use an atom as below:
(let [i (atom nil)
f (fn [] (meta #i))]
(reset! i f)
(prn 'before '>> (#i))
(swap! i with-meta {:rr 5})
(prn 'after '>> (#i)))
However, other than learning how these things fit together, I'm not sure what the goal is. It's probably a bad idea to try and use these structures in a real program that you plan on maintaining.
Rather accidentally, I found a trick that enables functions to read it own metadata. It appears, the Clojure compiler generates metadata support code differently when the original function definition has custom metadata. If it is present, (meta fn-name) works inside the body of the function, otherwise it does not. For example, the following produces the result desired by the OP:
*clojure-version*
;;=> {:major 1, :minor 10, :incremental 0, :qualifier nil}
(let [f1 ^{:foo true} (fn f [] (meta f))
f2 (with-meta f1 {:bar true})]
(prn (f1))
(prn (f2)))
;;=> {:foo true}
;;=> {:bar true}
;;=> nil
We can examine the code generated for a function without the metadata in the original definition - there is just the invoke method
(require '[clojure.pprint :as p])
(let [ff (fn f [] (meta f))]
(p/pprint (seq (.getDeclaredMethods (class ff)))))
;;=> (#object[java.lang.reflect.Method 0x2b56b137 "public java.lang.Object user$eval2171$f__2172.invoke()"])
;;=> nil
And when the metadata is present, additional methods (meta and withMeta) are generated to deal with the metadata.
(let [ff ^:foo (fn f [] (meta f))]
(p/pprint (seq (.getDeclaredMethods (class ff)))))
;;=> (#object[java.lang.reflect.Method 0x3983bd83 "public clojure.lang.IObj user$eval2175$f__2176.withMeta(clojure.lang.IPersistentMap)"]
;;=> #object[java.lang.reflect.Method 0x547d182d "public clojure.lang.IPersistentMap user$eval2175$f__2176.meta()"]
;;=> #object[java.lang.reflect.Method 0x62c3d0fe "public java.lang.Object user$eval2175$f__2176.invoke()"])
;;=> nil
Welcome to Clojure, #xstreamer!
I'm going to suggest something different from what (precisely) you're asking for. I don't know how querying the function's metadata from within the function should work, really. So I'm going to suggest defining the function first, and redefining the function metadata afterwards. This is fairly simple in Clojure.
(defn f
"Boring doc"
[])
(meta #'f)
;; => {:arglists ([]),
;; :doc "Boring doc",
;; :line 32,
;; :column 1,
;; :file "C:/Users/teodorlu/IdeaProjects/th-scratch/src/th/play/core.clj",
;; :name f,
;; :ns #object[clojure.lang.Namespace 0x3b402f0c "th.play.core"]}
Now, redefine it!
(alter-meta! #'f assoc :rr 5)
(meta #'f)
;; => {:arglists ([]),
;; :doc "Boring doc",
;; :line 32,
;; :column 1,
;; :file "C:/Users/teodorlu/IdeaProjects/th-scratch/src/th/play/core.clj",
;; :name f,
;; :ns #object[clojure.lang.Namespace 0x3b402f0c "th.play.core"],
;; :rr 5}
Where assoc sets a value in a map.
(assoc {} :rr 5)
;; {:rr 5}
(assoc {:some :stuff} :more :stuff)
;; {:some :stuff, :more :stuff}
References
If you're confused by the #'f, this is how you get the var representing the binding of f, instead of just the value it refers to. For more information about vars and how to use them, refer to the official reference on vars and the less terse guide from 8th light.
In my app I'm providing some interface to users that they can provide code and app evaluates that code within sandbox(so eval fn not allowed).The thing is I need to catch if user overrides some built-in function such as =
Any ideas how to catch and prevent that thing?(The idea is they should not be able to do that)
Code:
(defn =
[]
//some code)
WARNING: = already refers to: #'clojure.core/= in namespace: user, being replaced by: #'user/=
One solution might be:
I was trying to get the warning message as String but with-out-str function did not work.
(with-out-str
(defn = []))
;=> ""
Also wrote that with-err-str(changed with-out-str little bit) did not work as well.
(defmacro with-err-str
[& body]
`(let [s# (new java.io.StringWriter)]
(binding [*err* s#]
~#body
(str s#))))
(with-err-str
(defn = []))
;=> ""
Need: "WARNING: = already refers to: #'clojure.core/= in namespace: user, being replaced by: #'user/="
It does work when you use eval:
user=> (with-err-str (eval '(defn - [] 11)))
"WARNING: - already refers to: #'clojure.core/- in namespace: user, being replaced by: #'user/-\n"
user=> (re-seq #"WARNING" (with-err-str (eval '(defn / [] 11))))
("WARNING")
Or you could redefine the defn macro in user's code, but nothing prevents them to use other clojure tools to redefine a var:
user=> (defmacro defn-safe
#_=> [nam & decls]
#_=> (if (resolve (symbol "clojure.core" (name nam)))
#_=> (print "Whoops")
#_=> (list* `defn (with-meta nam (assoc (meta nam) :private true)) decls)))
#'user/defn-safe
user=> (defn-safe foo [x] (+ x 2))
#'user/foo
user=> (foo 22)
24
user=> (defn-safe = [a b] (- a b))
Whoopsnil
user=>
Another option, and probably your best bet is using
https://github.com/clojure/tools.analyzer
clojail handles this (and many other things as well). If you're looking to sandbox Clojure, I'd recommend taking a look.
One solution might be like this:
(def before (set (vals (ns-map *ns*))))
(defn = [])
(def after (set (vals (ns-map *ns*))))
(clojure.set/difference before after)
;=> #{#'clojure.core/=}
I have this code and would like to get the metadata transform
(defn truncate
[& {:keys [len]}]
(fn ^:transform [value]
(clojure.string/join (take len value))))
Ex: (meta (var (truncate)) //doesn't work
Something like this is possible? (meta (meta (var truncate))
UPDATE:
I moved it top the function name and solved it this way:
(defn- func-meta [func]
(let [[name-space func-name _] (clojure.string/split (str func) #"\$")]
(meta (ns-resolve (symbol name-space) (symbol func-name)))))
(func-meta (transform/truncate)) ;=> metadata
Attaching this type of metadata to arglists has no particular meaning in Clojure. (Type hints may be attached to arglists, but that's a different matter.) You can, however, attach metadata to the function itself using either of the following methods:
(defn foo []
^:foo (fn [] 1))
(defn foo []
(with-meta (fn [] 1) {:foo 1}))
;; in either case:
(meta (foo))
;= {:foo true}
Also, the var special form gives convenient access to Vars:
(var +)
;= #'clojure.core/+
The #' shorthand notation is used much more frequently.
I'm new to Clojure, and am trying to redirect output to a file by rebinding *out*. In a simple case, it works nicely:
(binding [*out* (new java.io.FileWriter "test.txt")]
(println "Hi"))
This does what I expect, printing "Hi" to the file test.txt. However, if I introduce a for loop, things go awry:
(binding [*out* (new java.io.FileWriter "test.txt")]
(for [x [1 2]]
(println "Hi" x)))
This time, all the output goes to stdout, and the file is empty. What's going on here?
I'm using Leiningen, if that makes any difference:
Leiningen 2.0.0 on Java 1.7.0_13 Java HotSpot(TM) 64-Bit Server VM
you have been bitten by the lazy bug.
put a doall or dorun around the for and within the binding
(binding [*out* (new java.io.FileWriter "test.txt")]
(doall (for [x [1 2]]
(println "Hi" x))))
In your example the printing is happening then the result is printed by the repl after it is returned from the binding. So at the time of printing the binding is no longer in place.
Nothing is printed because result is a lazy sequence that will later be evaluated when used
user> (def result (binding [*out* (new java.io.FileWriter "test.txt")]
(for [x [1 2]]
(println "Hi" x))))
#'user/result
When the repl prints the resuls the printlns are evaluated:
user> result
(Hi 1
Hi 2
nil nil)
If we force the evaluation of the lazy sequence returned by for within the binding
nothing is printed to the repl,
user> (def result (binding [*out* (new java.io.FileWriter "test.txt")]
(doall (for [x [1 2]]
(println "Hi" x)))))
#'user/result
user> result
(nil nil)
and instead the output ends up in the file:
arthur#a:~/hello$ cat test.txt
Hi 1
Hi 2
Here is the sample code I want to get to work:
(letfn [(CONC [f] f)
(CONT [f] (str "\newline" f))]
((voodoo "CONC") "hamster"))
Is there some voodo that will make it call the CONC function with hamster as the parameter? That is, is there some way to convert the string "CONC" into a function that is not bound to a namespace but rather to a local binding?
EDIT:
To be clearer, the way this will be called is:
(map #((voodoo (:tag %)) (:value %))
[
{:tag "CONC" :value "hamster"}
{:tag "CONT" :value "gerbil"}
]
)
I'd probably solve this by creating a map of functions indexed by strings:
(def voodoo
{"CONC" (fn [f] f)
"CONT" (fn [f] (str "\newline" f))})
Then your desired code should work directly (exploiting the fact that a map is a function that looks up it's argument)
(map #((voodoo (:tag %)) (:value %))
[
{:tag "CONC" :value "hamster"}
{:tag "CONT" :value "gerbil"}
]
)
Note that the functions here are fully anonymous - you don't need them to be referenced anywhere in the namespace for this to work. In my view this is a good thing, because unless you also need the functions somewhere else then it's best to avoid polluting your top-level namespace too much.
No. Eval does not have access to the local/lexical environment, ever.
Edit: This is not a very good answer, and not really accurate either. You could write voodoo as a macro, and then it doesn't need runtime access to the lexical environment, just compile-time. However, this means it would only work if you know at compile time that the function you want to call is x, and so it wouldn't be very useful - why not just type x instead of (voodoo "x")?
(defmacro voodoo [fname]
(symbol fname))
(letfn [(x [y] (inc y))]
((voodoo "x") 2))
;; 3
(letfn [(x [y] (inc y))]
(let [f "x"]
((voodoo f) 2)))
;; error
Well, it's sort of possible:
(defmacro voodoo [s]
(let [env (zipmap (map (partial list 'quote) (keys &env))
(keys &env))]
`(if-let [v# (~env (symbol ~s))]
v#
(throw (RuntimeException. "no such local")))))
...and now we can do weird stuff like this:
user> (defn example [s]
(letfn [(foo [x] {:foo x})
(bar [x] {:bar x})]
((voodoo s) :quux)))
#'user/example
user> (example "foo")
{:foo :quux}
user> (example "bar")
{:bar :quux}
user> (example "quux")
; Evaluation aborted.
user> *e
#<RuntimeException java.lang.RuntimeException: no such local>
That "Evaluation aborted" means an exception was thrown.
You could also replace the throw branch of the if in voodoo with (resolve (symbol ~s)) to defer to the globals if no local is found:
(defmacro voodoo [s]
(let [env (zipmap (map (partial list 'quote) (keys &env))
(keys &env))]
`(if-let [v# (~env (symbol ~s))]
v#
(resolve (symbol ~s)))))
...and now this works with definition of example as above (though note that if you are experimenting at the REPL, you will need to recompile example after redefining voodoo):
user> (defn quux [x] {:quux x})
#'user/quux
user> (example "quux")
{:quux :quux}
Now, this is an abuse of Clojure's facilities which one would do well to try to do without. If one cannot, one should probably turn to evalive by Michael Fogus; it's a library which provides an "eval-with-locals" facility in the form of an evil function and a couple of utilities. The functionality seems to be well factored too, e.g. something like the ~(zipmap ...) thing above is encapsulated as a macro and evil there appears to be almost a drop-in replacement for eval (add the env parameter and you're good to go). I haven't read the source properly, but I probably will now, looks like fun. :-)
Im not really clear what you are asking for so i'll try a couple answers:
if you have a string that is the name of the function you wish to call:
(def name "+")
((find-var (symbol (str *ns* "/" name))) 1 2 3)
this would give voodoo a deffinition like this:
(defn voodoo [name args] (apply (find-var (symbol (str *ns* "/" name))) args))
#'clojure.core/voodoo
clojure.core=> (voodoo "+" [1 2 3])
6
clojure.core=>
this assumes your function is in the current namepace ns.
if you want to turn a string into a function you could use this pattern
(let [f (eval (read-string "(fn [] 4)"))] (f))