Cooking with Lisp

Another blog about Lisp, the world's greatest programming language.

Thursday, February 02, 2006

How to Call a Setf Method Directly

Yesterday I wanted to wrap a library's setf method in another setf method that I was writing, but I wasn't sure how to directly call the other library's setf method.

Say I have the following:
(defclass foo ()
((foo-slot-1 :accessor foo-slot-1)))

(defparameter *a-foo* (make-instance 'foo))
Then if I macroexpand the call to (using SBCL):
(macroexpand-1 '(setf (foo-slot-1 *a-foo*) 99))
I get the following (with some cleaning up of the gensyms):
(let* ((#:temp1 *a-foo*))
(multiple-value-bind (#:temp0)
99
(funcall #'(setf foo-slot-1) #:temp0 #:temp1)))
;; i.e., (funcall #'(setf foo-slot-1) 99 *a-foo*)
That function argument #'(setf foo-slot-1) looks pretty strange, so I tried finding out what is going on here.

After a lot of research, here's what I found.

From the HyperSpec, funcall has the following syntax:
funcall function &rest args => result*
function---a function designator.
The glossary entry for 'function designator' isn't too helpful
function designator n. a designator for a function; that is, an object that denotes a function and that is one of: a symbol (denoting the function named by that symbol in the global environment), or a function (denoting itself). The consequences are undefined if a symbol is used as a function designator but it does not have a global definition as a function, or it has a global definition as a macro or a special form. See also extended function designator.
Since #'(setf a) is clearly not a symbol, it must be a function, but that's certainly a weird way to designate a function.

The entry for "extended function designator" isn't by itself any more useful, but provides a small clue:
extended function designator n. a designator for a function; that is, an object that denotes a function and that is one of: a function name (denoting the function it names in the global environment), or a function (denoting itself). The consequences are undefined if a function name is used as an extended function designator but it does not have a global definition as a function, or if it is a symbol that has a global definition as a macro or a special form. See also function designator.
The key here is "function name":
function name n. 1. (in an environment) A symbol or a list (setf symbol) that is the name of a function in that environment. 2. A symbol or a list (setf symbol).
Additionally, there's also these entries:
setf function n. a function whose name is (setf symbol).

setf function name n. (of a symbol S) the list (setf S).
Aha! So the only two ways to specify a function name is with a symbol (the case we're all familiar with) or the list (setf symbol).

So, we're pretty close. The list (setf foo-slot-1) is a setf function name. You'll note that funcall takes a function designator and not an extended function designator, that is, it takes functions, not function names. Of course, the way to get from function names to functions is to use the function "function" or the better know reader macro #'. Thus, we finally have #'(setf foo-slot-1), which is equivilent to (function (setf foo-slot-1)) (no quote needed for (setf foo-slot-1) because function is a special operator. So, if you type #'(setf foo-slot-1), you'll get back something like #.

The final oddity is the ordering of the arguments, it appears to be

(funcall #(setf symbol) newvalue oldvalue)

This is explained in the sections explaining how setf expansion works:
5.1.2.9 Other Compound Forms as Places

For any other compound form for which the operator is a symbol f, the setf form expands into a call to the function named (setf f). The first argument in the newly constructed function form is newvalue and the remaining arguments are the remaining elements of place. This expansion occurs regardless of whether f or (setf f) is defined as a function locally, globally, or not at all. For example,

(setf (f arg1 arg2 ...) new-value)

expands into a form with the same effect and value as
(let ((#:temp-1 arg1)          ;force correct order of evaluation
(#:temp-2 arg2)
...
(#:temp-0 new-value))
(funcall (function (setf f)) #:temp-0 #:temp-1 #:temp-2...))
A function named (setf f) must return its first argument as its only value in order to preserve the semantics of setf.
Putting the new value first allows everything following to look just like the call to the accessing function, even if it has lots of additional lambda keyword arguments, for example

(setf (mumble object :keyword1 :keyword2) newvalue)

being transformed into something like

(funcall #'(setf mumble) newvalue :keyword1 :keyword2)

Anyway, I was able to successfully call the other library's setf method by following the above example.

My final thoughts, I have a huge amount of respect for the people able to read between the lines of the spec and be able to correctly implement all of the nooks and crannies of Common Lisp.

13 Comments:

At 1:17 PM, Blogger Glenn Ehrlich said...

Well, I have to admit my post looks pretty ugly.

Can anyone recommend hosted blogging that allows decent formatting of code snippets?

 
At 2:57 PM, Blogger Dave Roberts said...

Blogger will do what you want, but you'll probably have to use the HTML composition frame. Wrap the code in a <pre> tag and escape all <, >, and & with proper HTML entities (that's in fact what I just did to write this comment). Take a look on my blog here for some handy Emacs functions to do this automatically.

 
At 3:51 PM, Blogger Tim Moore said...

Why weren't you able to either write an around method on the setf method you're wrapping or call the setf macro again in the normal way? Either you know that the foreign library defines a setf method you can specialize -- because that's part of the documented interface -- or you don't, in which case you can't assume that #'(setf foo) exists.

 
At 9:43 PM, Blogger Glenn Ehrlich said...

An :around method wouldn't work because I have a different signature (I'm wrapping some of the functionality of the original library to simplify it), however calling the setf directoy, d'oh! I feel pretty silly. My only excuse is that I've started a real serious diet and I'm definitely not as mentally sharp as I normally am :(

 
At 1:24 AM, Blogger ogmos74 said...

Dad, I was bored and found your blog on Google. Your like Yoda when it comes to this stuff.

 
At 2:25 PM, Blogger its mortgage magic said...

This is a good point but when you deal with this stuff on a regular basis. You tend to figure it all out when you study html code. Many tricks in the bag. Cody

 
At 6:23 AM, Blogger Shape said...

i'm happy read your blog
http://adware-spyware-uninstall.blogspot.com/

 
At 3:05 AM, Anonymous nick said...

Complicated language.

 
At 3:13 AM, Anonymous Elisa said...

Wow, interestig thing!

 
At 12:52 PM, Blogger hajarwan said...

Hi,
I recently came across your blog and have been reading along. I thought I would leave my first comment. Great post, concise and easy to understand. I like this post..

I found out that this blog is very interesting and informative.
Best of luck to you!

Cheers,
Health and Lifestyle
Budget Hotels and Resorts
Top Fishing Games
Pearl Necklaces

 
At 9:09 PM, Blogger an said...

Hi,

Nice post! Your content is very valuable to me and just make it as my reference.Keep blogging with new post!Unique and useful to follower....

Cheers,

car audio schools

 
At 9:33 PM, Blogger Garret said...

Yea the information is great! Good Job! As far as the look goes I would suggest what Dave Roberts said!

Good luck and keep up the posts!

~Garret
copycat cookbook
Glitter Toes Utah
math games for the classroom
Altnat Roofing

Keep up the good work! Thanks!

 
At 12:26 AM, Anonymous Free Poker Money said...

This is one of the most interesting articles since few months on your blog. Congratulations dear admin.
-----------------------------------
No Deposit Poker
http://www.pokerbonussansdepot.biz
The traditional, the most common variant of the game is to play each hand of five cards. After they watched the players to change the surround, ie increase pool game for a certain amount of money, tokens or symbolic matches. After this step can include, or not, of 1 to 4 cards. In some embodiments, the game is possible to replace all the cards. After this round of betting occurs again. If none of the players have not folded, teaches cards, and accumulated a pool player takes the top card system. The hierarchy system is as follows: "the highest card" - wins the oldest of all visible on the table starting from Ace to Two, in case of a tie - determines the next card, and so until the fifth. "Pair" - two cards of one type, "two people", "three" - three cards of the same type "," straight "- five consecutive cards of different colors, the lowest straight is from 2 to 5, the oldest - from 10 to ace, "color" - five cards of the same suit, hearts, diamonds, clubs and spades, "Full House" - three and a pair of, about the importance of a full house with the higher three, "carriage" - four cards of the same type and one "poker" - straight in color. The highest poker hand is "royal flush" or cards from 10 to Ace of the same suit.
Gratis Online Poker
Poker ohne einzahlung
This variation of the game has become very popular because of the multitude of tournaments planted by excellent players, and many web sites devoted to this type of game. In poker, texas hold 'em get to own only two cards, followed by a round of betting. Then the dealer puts three cards on the table common to all - "the flop" betting again, another community card - "turn", auction and last community card - "the river". It is followed by the last round of betting, and if it has been more than one player with the cards in his hand wins the best 5 card hand built with two player cards and three of the five common. The hierarchy of the system is the same as in poker classic. In both variants of the game can be in the form of plant diversity "in the dark", before looking at cards, which greatly affects the dynamics of the game. In most games, Texas Hold'em, two players must post a blind, the so-called big and small "blind"
Dana Witchwood
Nick Franck

 

Post a Comment

<< Home