I'm making a math game. To store math questions, I'm using the following record:
(defrecord Question [args operator]
Object
(toString [self]
(apply str (drop-last 2 (interleave args (repeat \space) (repeat operator) (repeat \space))))))
The toString "method" converts the args and operator into a infix-operator string.
My problem is, when I attempt to convert a function (like *) to a string, I get a qualified, Java-compliant symbol name:
(str *)
"clojure.core$_STAR_#a2a3e7"
All I want though is to literally convert the symbol to a string, so the * function yields "*". The best I've been able to do is use a pre-defined mapping from function-symbol to string; but ideally I'd like to not need the map and just generate the string version of the symbol directly.
Is this possible?
You're confusing the symbol that you can get by evaluating '* with the function that you can get by evaluating *:
'*
;;=> *
*
;;=> #function[clojure.core/*]
Of course, '* isn't the only way to get the symbol *. Instead of using the ' reader macro, you could use the equivalent quote special form, or you could use the symbol function to create a symbol from a string, or you could do something else:
(quote *)
;;=> *
(symbol "*")
;;=> *
If you convert a symbol to a string, you get back a string consisting of the name (and, if present, the namespace) of that symbol . If you convert a function to a string, you get back the default Java string representation of an object: the name of its class, followed by the # character and a hexadecimal representation of its location in memory:
(str '*)
;;=> "*"
(str *)
;;=> "clojure.core$_STAR_#a2a3e7"
So if you create a Question with the function * as its operator and then try to convert that function to a string, you'll get this:
(str (->Question [4 2] *))
;;=> "4 clojure.core$_STAR_#a2a3e7 2 "
On the other hand, if you create a Question with the symbol * as its operator, you'll get this instead:
(str (->Question [4 2] '*))
;;=> "4 * 2 "
Given a symbol, you can resolve it to a var using resolve, then get that var's value using deref:
#(resolve '*)
;;=> #function[clojure.core/*]
You can't generally go the other way (from a function to a symbol), though. See the answers to this question for more details.
Fortunately / unfortunately I realized the answer right after posting this: macros.
(defmacro sym->string [sym]
(str sym))
(sym->string *)
"*"
Sorry, I guess I'm not quite awake yet.
This is actually wrong: see the comment below.
Related
Why do I get the error:
IllegalArgumentException First argument to defn must be a symbol clojure.core/defn (core.clj:277)
When I try to define a function like this:
(defn (symbol "f[]") 1)
Or like this:
(defn (symbol "f") [] 1)
Why aren't those the equivalent of straight forward example below ?
(defn f [] 1)
This is esoteric I know: but it just occurred to me that I might want to name a function dynamically at some point. (No real use case here - just trying to understand Clojure's mind...)
When you pass arguments to a macro, they are not evaluated beforehand. Since defn is a macro, what you're passing it in those two cases are not equivalent.
You are mixing code and data. It is a very common mistake to do. Eg.
(+ 4 5) ; ==> 9
('+ 4 5) ; ==> Error
'+ evaluates to a symbol. It is not the same as the variable + that is code and evaluates for a function. It's easy to check by evaluating them:
+ ; ==> #<core$_PLUS_ clojure.core$_PLUS_#312aa7c>
'+ ; ==> +
defn is a macro that expands to def so your beef is with def. The reason (def (symbol "x") 5) doesn't work is because def happens at compile time. The first arguments is never evaluated, but used for all references to the same identifiers within the same namespace. An expression like (symbol "x") won't work pretty much because of the same reason + and '+ cannot be mixed. You can do this in compile time though:
(defmacro make-fun [name expression]
`(defn ~(symbol name) [] ~expression))
(macroexpand-1 '(make-fun "f" 1))
; ==> (clojure.core/defn f [] 1)
(make-fun "f" 1)
; ==> #'user/f
(f) ; ==> 1
So what is happening is that before the code runs (make-fun "f" 1) gets replaced with (clojure.core/defn f [] 1) and the runtime never ever sees where it came from. While this seems useful you still cannot use a binding or input to make your function:
(def fun-name "f")
(def fun-value 1)
(macroexpand-1 '(make-fun fun-name fun-value))
; ==> (clojure.core/defn fun-name [] fun-value)
Macros are just a way to simplify and abstract on syntax. If you always write a pattern that looks like (defn name [& args] (let ...) you can make the parts that differ bindings in a macro and shorten every place you use the abstraction with the new macro. It is a code translation service. In compile time the arguments are just the literal code that it is suppsoed to replace and you never have the luxury to see if a variable or expression has a certain value since you only knows about the code and never what they actually represent. Thus the errors usually arises in when the code in the end result runs.
In the end you can do anything in runtime with eval. I've seen eval being used in a sensible manner twice in my 19 year run as a professional programmer. You could do:
(defn make-fun [name value]
(eval `(defn ~(symbol name) [] ~value)))
(make-fun fun-name fun-value)
; #'user/f
(f)
; ==> 1
Now while this works you shouldn't do it unless this is some sort of tool to test or do something with code rather than it being a part of the code to be run as a service with the string coming in from a unsafe source. I would have opted for using dictionaries instead such that you do not update your own environment. Imagine if the input was make-fun or some other part of your code that would give the client control over your software.
The answer is what Josh said (defn is a macro; if it was a function then your code really would work in this way). You can define your own defn variation macro that would do what you want or just use eval:
(eval `(defn ~(symbol "f") [] 1))
; => #'user/f
(f)
; => 1
You really don't need to use eval.
You have hit the problem known as "turtles all the way down". Once you try to treat a macro like a function (perhaps passing it to map, for example), you find you cannot do it without writing another macro. The same applies to macro #2, etc.
Thus, you can't compose macros as well as you can compose functions. This is the genesis of the general advice, "Never use a macro when you can use a function."
In this case, defn is a macro, so you have no choice but to write another macro (def behaves the same way, even though it is a special form instead of a macro). Our new macro dyn-defn dynamically creates the function name from a list of strings:
(defn fun-1 [] 1)
(def fun-2 (fn [] 2))
; (def (symbol (str "fun" "-3")) (fn [] 3))
; => Exception: First argument to def must be a Symbol
(defmacro dyn-defn
"Construct a function named dynamically from the supplied strings"
[name-strs & forms]
(let [name-sym (symbol (str/join name-strs)) ]
(spyx name-sym)
`(defn ~name-sym ~#forms)))
(dyn-defn ["fun" "-3"]
[]
3)
with result:
*************** Running tests ***************
:reloading (tst.demo.core)
name-sym => fun-3 ; NOTE: this is evaluated at compile-time
Testing _bootstrap
-------------------------------------
Clojure 1.9.0 Java 1.8.0_161
-------------------------------------
Testing demo.core
Testing tst.demo.core
(fun-1) => 1 ; NOTE: these are all evaluated at run-time
(fun-2) => 2
(fun-3) => 3
Note that the function name is an argument to the defn macro, and must be a symbol, not a function call.
Note:
Correct, you can't tell by looking at it if a form is "calling" a function or a macro. In fact, many "build-in" features of Clojure are constructed from more fundamental parts of the language, whether macros like when (source code) or functions like into (source code).
I want to get following results when I evaluate edit-url and (edit-url 1).
edit-url --> "/articles/:id/edit"
(edit-url 1) --> "/articles/1/edit"
Is it possible to define such a Var or something?
Now, I use following function, but I don't want to write (edit-url) to get const string.
(defn edit-url
([] "/articles/:id/edit")
([id] (str "/articles/" id "/edit")))
Thanks in advance.
If those behaviors are exactly what you want, print-method and tagged literals may be used to imitate them.
(defrecord Path [path]
clojure.lang.IFn
(invoke [this n]
(clojure.string/replace path ":id" (str n))))
(defmethod print-method Path [o ^java.io.Writer w]
(.write w (str "#path\"" (:path o) "\"")))
(set! *data-readers* (assoc *data-readers* 'path ->Path))
(comment
user=> (def p #path"/articles/:id/edit")
#'user/p
user=> p
#path"/articles/:id/edit"
user=> (p 1)
"/articles/1/edit"
user=>
)
edit-url will either have the value of an immutable string or function. Not both.
The problem will fade when you write a function with better abstraction that takes a string and a map of keywords to replace with words. It should work like this
(generate-url "/articles/:id/edit" {:id 1})
Clojure is a "Lisp 1" which means that is has a single namespace for all symbols, including both data scalars and functions. What you have written shows the functionally of both a string and a function but for a single name, which you can do in Common Lisp but not Clojure (not that a "Lisp 2" has its own inconveniences as well).
In general this type of "problem" is a non issue if you organize your vars better. Why not just make edit-url a function with variable arity? Without arguments it returns something, with arguments it returns something else. Really the possibilities are endless, even more so when you consider making a macro instead of a function (not that I'm advocating that).
I have a function
(defn hi [a] '[a 2])
which should take a value a and insert the value in a quoted vector, and return the vector back.
So calling (hi :abc) should return [:abc 2]. The important thing to note is that the internal vector is quoted. How do I do this?
This can help:
user=> (defn hi [a] [a '2])
#'user/hi
user=> (hi :abc)
[:abc 2]
If I understand you correctly, you want to be able to pass an undefined symbol to the function, and get back a vector containing that symbol (with some other stuff added).
If you quote the symbol before passing it to the function, then you don't have to do anything special inside the function:
user=> (defn hi [a] [a 2])
user=> (= (hi '?ab) '[?ab 2])
true
If you don't want to quote your symbols (e.g. you're making a DSL to enable idiomatic authoring of datomic query structures) then you have to use a macro, because eval'ing a function will cause the symbol to be resolved in the namespace. You could try something like:
user=> (defmacro hi2 [a] `(quote [~a 2]))
user=> (= (hi2 ?ab) '[?ab 2])
true
I am trying to use clojure in a compiler and thus need to parameterize calls to deftype; however, I am having difficulty making the type hints carry through. Consider the following code:
(defn describe [x]
(let [fields (.getDeclaredFields x)
names (map #(.getName %) fields)
types (map #(.getType %) fields)]
(interleave types names)))
(defn direct [] (deftype direct-type [^int x]))
(defn indirect-helper [] (list ^int (symbol "x")))
(defn indirect [] (eval `(deftype ~(symbol "indirect-type") ~(indirect-helper))))
And the following session from the REPL:
Clojure 1.2.0-master-SNAPSHOT
1:1 user=> #<Namespace dataclass>
1:2 dataclass=> (direct)
dataclass.direct-type
1:3 dataclass=> (indirect)
dataclass.indirect-type
1:4 dataclass=> (describe direct-type)
(int "x")
1:5 dataclass=> (describe indirect-type)
(java.lang.Object "x")
Notice that the generated class for indirect-type has lost the ^int hints that direct-type has. How do I get those hints to carry through?
You'll need to change indirect-helper to read
(defn indirect-helper [] [(with-meta (symbol "x") {:tag 'int})])
The reason is that ^int parses as ^ followed by int; ^, in Clojure 1.2, introduces reader metadata (in 1.1 you'd use #^, which still works, but is deprecated in 1.2). Thus ^int x in direct gets read in as a clojure.lang.Symbol whose name is "x" and whose metadata map is {:tag int} (with the int here being itself a symbol). (The final component of a symbol -- its namespace -- is nil in this case.)
In the version of indirect-helper from the question text ^int gets attached to (symbol "x") -- the list comprising the symbol symbol and the string "x" (meaning in particular that (list ^int (symbol "x")) evaluates to a list of 1 element). This "type hint" is lost once (symbol "x") is evaluated. To fix things, some way to attach metadata to the actual symbol generated by (symbol "x") is needed.
Now in this case, the symbol is generated at runtime, so you can't use reader metadata to attach the type hint to it. Enter with-meta, which attaches metadata at runtime (and is frequently useful in writing macros for the same reason as it is here) and the day is saved:
user> (indirect)
user.indirect-type
user> (describe indirect-type)
(int "x")
(BTW, I thought deftype expected a vector of field names, but apparently a list works as well... A vector is still certainly more idiomatic.)
I have a symbol "a" bound to a function:
(defn a []
(println "Hello, World"))
user=> a
#<user$a__292 user$a__292#97eded>
user=> (a)
Hello, World
nil
Then I use syntax-quote, it "resolves the symbol in the current context, yielding a fully-qualified symbol", according to Clojure documentation. But why can't I use it the same way as unqualified symbol?
user=> `a
user/a
user=> (`a)
java.lang.IllegalArgumentException: Wrong number of args passed to: Symbol (NO_SOURCE_FILE:0)
Second question: if I have a symbol in a list, why can't I evaluate it the same way as if I would evaluate the symbol directly?
user=> (def l '(a 1 2))
#'user/l
user=> 'l
l
user=> (first l)
a
user=> ((first l))
java.lang.IllegalArgumentException: Wrong number of args passed to: Symbol (NO_SOURCE_FILE:0)
I have a suspicion I have a fatal flaw somewhere in the fundamental understanding of how symbols work here. What is wrong with above code?
REPL = read eval print loop. Step through the read-eval process.
READ: Clojure sees the string "(`a)", parses it and ends up with a data structure. At read time, reader macros are expanded and not much else happens. In this case, the reader expands the backquote and ends up with this:
user> (read-string "(`a)")
((quote user/a))
EVAL: Clojure tries to evaluate this object. Evaluation rules vary depending on what kind of object you're looking at.
Some objects evaluate as themselves (numbers, strings, keywords etc.).
A Symbol is evaluated by resolving it in some namespace to obtain some value (usually).
A List is evaluated by macro-expanding the list until there are no macros left, then recursively evaluating the first item in the list to obtain some resulting value, then using the value of the first item in the list to decide what to do. If the first value is a special form, special stuff happens. Otherwise the first value is treated as a function and called with the values of the rest of the list (obtained by recursively evaluating all of the list's items) as parameters.
etc.
Refer to clojure.lang.Compiler/analyzeSeq in the Clojure source to see the evaluation rules for lists, or clojure.lang.Compiler/analyzeSymbol for symbols. There are lots of other evaluation rules there.
Example
Suppose you do this:
user> (user/a)
The REPL ends up doing this internally:
user> (eval '(user/a))
Clojure sees that you're evaluating a list, so it evaluates all items in the list. The first (and only) item:
user> (eval 'user/a)
#<user$a__1811 user$a__1811#82c23d>
a is not a special form and this list doesn't need to be macroexpanded, so the symbol a is looked up in the namespace user and the resulting value here is an fn. So this fn is called.
Your code
But instead you have this:
user> (eval '((quote user/a)))
Clojure evaluates the first item in the list, which is itself a list.
user> (eval '(quote user/a))
user/a
It evaluated the first item in this sub-list, quote, which is a special form, so special rules apply and it returns its argument (the Symbol a) un-evaluated.
The symbol a is the value in this case as the fn was the value up above. So Clojure treats the Symbol itself as a function and calls it. In Clojure, anything that implements the Ifn interface is callable like an fn. It so happens that clojure.lang.Symbol implements Ifn. A Symbol called as a function expects one parameter, a collection, and it looks itself up in that collection. It's meant to be used like this:
user> ('a {'a :foo})
:foo
This is what it tries to do here. But you aren't passing any parameters, so you get the error "Wrong number of args passed to: Symbol" (it expects a collection).
For your code to work you'd need two levels of eval. This works, hopefully you can see why:
user> (eval '((eval (quote user/a))))
Hello, world
user> ((eval (first l)))
Hello, world
Note that in real code, using eval directly is usually a really bad idea. Macros are a better idea by far. I'm only using it here for demonstration.
Look in Compiler.java in the Clojure source to see how this all plays out. It's not too hard to follow.
Using a Symbol as a function is not the same thing as evaluating it. Symbols-as-functions work the same way as keywords-as-functions. Like this:
user=> (declare a)
#'user/a
user=> (def a-map {'a "value"})
#'user/a-map
user=> ('a a-map)
"value"
user=>
This is not how you would normally use a symbol. They are more commonly used for looking up vars in a namespace, and when generating code in a macro.
To break down the layers of indirection, let's define "x" as 1 and see what happens:
user=> (def x 1)
#'user/x
Using def, we have created a "var." The name of the var is the symbol user/x. The def special form returns the var itself to the repl, and this is what we can see printed. Let's try and get a hold of that var:
user=> #'x
#'user/x
The #' syntax is a reader macro that says "give me the var referred to by the following symbol." And in our case, that symbol is "x". We got the same var back as before. Vars are pointers to values, and can be dereferenced:
user=> (deref #'x)
1
But the var needs to be found before it can be dereferenced. This is where the callability of symbols come into play. A namespace is like a map, where the symbols are keys and vars are the values, and when we plainly name a symbol, we implicitly look up its var in our namespace. Like this:
user=> ('x (.getMappings *ns*))
#'user/x
Although, in reality, it is probably more like this:
user=> (.findInternedVar *ns* 'x)
#'user/x
And now we have come full circle on the journey of the unquoted symbol:
user=> (deref (.findInternedVar *ns* 'x))
1
user=> x
1
The two are not entirely equal, though. Because the evaluator does this for all symbols, including deref and *ns*.
The thing about quoting is that you essentially bypass this whole mechanism, and just get the plain symbol back. Like the #' reader macro get plain vars back, the ` and ' reader macros will get plain symbols back, with or without a namespace qualification respectively:
user=> 'x
x
user=> `x
user/x
user=> (def l '(a 1 2))
user=> ((first l))
Turn this into:
user=> (def l `(~a 1 2))
The ~ here resolves the symbol a to its corresponding var, and the backtick makes unquoting work.
In general, you must understand the difference between vars (which are bound to something) and symbols (which are never bound to anything).
I'll try to explain it (in the hope that my exaplanation does not confuse you further):
user=> (def v "content")
#'user/content
-> defines a var in the current namespace under the symbol 'v (fully qualified 'user/v, assuming this is the current namespace), and binds it (the var, not the symbol) to the object "content".
user=> v
"content"
-> resolves v to the var, and gets the bound value
user=> #'v
#'user/v
-> resolves to the var itself
user=> 'v
v
-> does not resolve anything, just a plain symbol (unfortunately, the REPL does not indicate this, printing 'v as v)
user=> `v
user/v
-> as you already quoted, resolves to the symbol in the current context (namespace), but the result is still a symbol (fully qualified), not the var user/v
user=> '(v)
(v)
-> plain quoting, does not resolve anything
user=> `(v)
(user/v)
-> syntax-quote, same as quoting, but resolves symbols to namespace-qualified symbols
user=> `(~v)
("content")
-> resolve the symbol to its var (which is implicitely dereferenced), yielding its bound object