understanding quoting

75 views
Skip to first unread message

Mark Volkmann

unread,
Dec 15, 2008, 9:41:26 PM12/15/08
to clo...@googlegroups.com
Here's a summary of what I think I know about quoting in Clojure. I'd
appreciate some feedback if I said anything wrong below or maybe
didn't describe something well.

; One use of quoting is to prevent a list from being evaluated
; where the first item is treated as the name of a function
; and the remaining items are treated as arguments to the function.

(println '(+ 1 2)) ; outputs (+ 1 2) instead of 3 due to the quote
(println (quote (+ 1 2))) ; same

; Quoting a list is *not* the same as quoting everyting inside it.
(println ('+ '1 '2)) ; outputs 2 which is the value of the last item evaluated

; Using a "syntax quote" produces a similar result,
; but the items are resolved to their fully-qualified names.
(println `(+ 1 2)) ; outputs (clojure.core/+ 1 2)

; In all these examples, the result can be bound to a variable
; and evaluated later.
(def demo1 '(+ 1 2))
(println (eval demo1)) ; outputs 3

; Particularly in macros it can be useful to
; evaluate a subset of the items inside a syntax quoted list.
; ~ (unquote) and ~@ (unquote-splicing) are used for this.
; ~@ splices in all the items in a collection.
; This doesn't work in quoted lists, only in syntax quoted lists.
; Note that while "quote" is the name of a real function,
; "unquote" and "unquote-splicing" are not.
(let [a 1, b [2 3]]
(println `(+ ~a ~@b c))) ; outputs (clojure.core/+ 1 2 3 user/c)

--
R. Mark Volkmann
Object Computing, Inc.

Chouser

unread,
Dec 15, 2008, 10:06:13 PM12/15/08
to clo...@googlegroups.com
On Mon, Dec 15, 2008 at 9:41 PM, Mark Volkmann
<r.mark....@gmail.com> wrote:
>
> ; Quoting a list is *not* the same as quoting everyting inside it.
> (println ('+ '1 '2)) ; outputs 2 which is the value of the last item evaluated

Your example works, but your comment is not always true:

user=> ('+ '1)
nil
user=> ('+ '1 '2 '3)
java.lang.IllegalArgumentException: Wrong number of args passed to:
Symbol (NO_SOURCE_FILE:0)
user=> ('b '{a 10, b 11, c 12})
11

--Chouser

Timothy Pratley

unread,
Dec 15, 2008, 10:47:32 PM12/15/08
to Clojure
What is the meaning of:
('+ '1 '2)

On the surface it appears that '2 is simply the last evaluated, but
lets try some similar calls:
user=> ('+)
java.lang.IllegalArgumentException: Wrong number of args passed to:
Symbol
user=> ('+ '1)
nil
user=> ('+ '1 '2)
2
user=> ('+ '1 '2 '3)
java.lang.IllegalArgumentException: Wrong number of args passed to:
Symbol
user=> ('1 '2 '3)
java.lang.ClassCastException: java.lang.Integer cannot be cast to
clojure.lang.Fn


Non-empty Lists are considered calls to either special forms, macros,
or functions. A call has the form (operator operands*).
'+ is just (quote +) which returns a Symbol. That Symbol is being
treated as a function which can have 1 or 2 arguments. My guess this
is actually looking up a symbol in the symbol list???

user=> ('a 2 +)
#<core$_PLUS___230 clojure.core$_PLUS___230@8d0b0f>


Regards,
Tim.


Timothy Pratley

unread,
Dec 15, 2008, 11:04:44 PM12/15/08
to Clojure
> user=> ('b '{a 10, b 11, c 12})
> 11

Ah, yes so the 1 arg version is the map lookup, which also works in
reverse
user=> ('{a 10, b 11, c 12} 'b)
11
That makes perfect sense...

What is the 2 arg version?
user=> ('{a 10, b 11, c 12} 'b 'c)
11
user=> ('b '{a 10, b 11, c 12} 'c)
11
user=> ('b 'c '{a 10, b 11, c 12})
{a 10, c 12, b 11}
user=> ('d '{a 10, b 11, c 12} 'c)
c

Looks like an if then else version of the map lookup??
ie: (if (%1 %2) (%1 %2) %3)
Is this a special feature of maps in general, such that you can look
up a key but return something else if it doesn't exist?
I hadn't come across it yet, but it sounds useful :)


Regards,
Tim.

Daniel Eklund

unread,
Dec 15, 2008, 11:43:40 PM12/15/08
to Clojure
> Looks like an if then else version of the map lookup??
> ie: (if (%1 %2) (%1 %2) %3)
> Is this a special feature of maps in general, such that you can look
> up a key but return something else if it doesn't exist?
> I hadn't come across it yet, but it sounds useful :)

This is exactly right (I just confirmed it for myself). I looked up
the java code for Keyword.java and Symbol.java both of which implement
IFn (those things that can be put in the function position). The
single and double arity invoke() merely delegate to 'get'. And voila:

user> (doc get)
-------------------------
clojure.core/get
([map key] [map key not-found])
Returns the value mapped to key, not-found or nil if key not
present.

The last parameter is definitely for those situations where 'if not
found, use this'

I think I _finally_ understand the statement, "keywords and symbols
are functions of maps".

So, the original poster's observation about ('+ '1 '2) returning '2
makes more sense when you consider that it turns into this:

(get '1 '+ '2)

or "get from the hashmap ( '1 ) the value stored at key ( '+ ) and if
nil is returned, return ( '2 ) instead"

(get '1 '+) returns nil as '1 is not even a map.

Brian Doyle

unread,
Dec 16, 2008, 12:01:45 AM12/16/08
to clo...@googlegroups.com
On Mon, Dec 15, 2008 at 9:43 PM, Daniel Eklund <doek...@gmail.com> wrote:

> Looks like an if then else version of the map lookup??
> ie: (if (%1 %2) (%1 %2) %3)
> Is this a special feature of maps in general, such that you can look
> up a key but return something else if it doesn't exist?
> I hadn't come across it yet, but it sounds useful :)

This is exactly right (I just confirmed it for myself).  I looked up
the java code for Keyword.java and Symbol.java both of which implement
IFn  (those things that can be put in the function position).  The
single and double arity invoke() merely delegate to 'get'.  And voila:

How did you know that it delegates to 'get'?
 

Timothy Pratley

unread,
Dec 16, 2008, 12:07:22 AM12/16/08
to Clojure
Neat :) Thanks for the in depth examination, that's a very clear
explanation!

Daniel Eklund

unread,
Dec 16, 2008, 12:12:04 AM12/16/08
to Clojure
> How did you know that it delegates to 'get'?


sorry, I rushed that part.

the keyword and symbol are instances of clojure.lang.Keyword and
clojure.lang.Symbol
which are _java_ classes found in Keyword.java and Symbol.java (I
found these in the src/jvm directory)


These are what the invoke( ) methods looks like for the single and
double arity of both these classes (this is _java_ code):

/**
* Indexer implements IFn for attr access
*
* @param obj - must be IPersistentMap
* @return the value at the key or nil if not found
* @throws Exception
*/
public Object invoke(Object obj) throws Exception{
return RT.get(obj, this);
}

public Object invoke(Object obj, Object notFound) throws Exception{
return RT.get(obj, this, notFound);
}


-----------------
which in turn goes to RT.java

static public Object get(Object coll, Object key){
if(coll == null)
return null;
else if(coll instanceof Associative)
return ((Associative) coll).valAt(key);
else if(coll instanceof Map)
{
Map m = (Map) coll;
return m.get(key);
}
else if(coll instanceof IPersistentSet)
{
IPersistentSet set = (IPersistentSet) coll;
return set.get(key);
}
else if(key instanceof Number && (coll instanceof String ||
coll.getClass().isArray()))
{
int n = ((Number) key).intValue();
if(n >= 0 && n < count(coll))
return nth(coll, n);
return null;
}

return null;
}
static public Object get(Object coll, Object key, Object notFound){
if(coll == null)
return notFound;
else if(coll instanceof Associative)
return ((Associative) coll).valAt(key, notFound);
else if(coll instanceof Map)
{
Map m = (Map) coll;
if(m.containsKey(key))
return m.get(key);
return notFound;
}
else if(coll instanceof IPersistentSet)
{
IPersistentSet set = (IPersistentSet) coll;
if(set.contains(key))
return set.get(key);
return notFound;
}
else if(key instanceof Number && (coll instanceof String ||
coll.getClass().isArray()))
{
int n = ((Number) key).intValue();
return n >= 0 && n < count(coll) ? nth(coll, n) : notFound;
}
return notFound;

}


---------

all really cool....

Mon Key

unread,
Dec 16, 2008, 2:48:26 AM12/16/08
to Clojure
For FAQ style plain text I like having the RHS comments moved below
the S-Expression as I can C-n down the file and do C-x C-e evaluation
to REPL as I go.

Once the RHS ;;;Comments are below the S-Expressions I find i like
having a symbol to indicate the eval =>

I took the liberty of re-formatting :)
;;; =======================
Here's a summary of what I think I know about quoting in Clojure. I'd
appreciate some feedback if I said anything wrong below or maybe
didn't describe something well.

; One use of quoting is to prevent a list from being evaluated
; where the first item is treated as the name of a function
; and the remaining items are treated as arguments to the function.

(println '(+ 1 2))
; => (+ 1 2) - instead of 3 due to the quote
(println (quote (+ 1 2)))
; => (+ 1 2) - same

; Quoting a list is *not* the same as quoting everyting inside it.
(println ('+ '1 '2))
; => 2 - outputs 2 which is the value of the last item evaluated

; Using a "syntax quote" produces a similar result,
; but the items are resolved to their fully-qualified names.
(println `(+ 1 2))
; => (clojure.core/+ 1 2)

; In all these examples, the result can be bound to a variable
; and evaluated later.
(def demo1 '(+ 1 2))
(println (eval demo1))
; => 3

; Particularly in macros it can be useful to
; evaluate a subset of the items inside a syntax quoted list.
; ~ (unquote) and ~@ (unquote-splicing) are used for this.
; ~@ splices in all the items in a collection.
; This doesn't work in quoted lists, only in syntax quoted lists.
; Note that while "quote" is the name of a real function,
; "unquote" and "unquote-splicing" are not.
(let [a 1, b [2 3]]
(println `(+ ~a ~@b c)))
; =>(clojure.core/+ 1 2 3 user/c)
Reply all
Reply to author
Forward
0 new messages