How does using the SETF function to extend SETF work?
Asked Answered
F

2

9

In Practical Common Lisp chapter 17. Object Reorientation: Classes section Accessor Functions, I was finding it difficult understanding the way SETF is being extended.

The functions:

(defun (setf customer-name) (name account)
  (setf (slot-value account 'customer-name) name))

bank-account class definition:

(defclass bank-account ()
  ((customer-name
    :initarg :customer-name
    :initform (error "Must supply a customer name."))
   (balance
    :initarg :balance
    :initform 0)
   (account-number
    :initform (incf *account-numbers*))
   account-type))

What I don't understand:

  • in the expression (setf (customer-name my-account) "Sally Sue") does (customer-name my-account) return a SETFable slot-value customer-name of the the class bank-account which then SETF uses to set the value to "Sally Sue"?

  • is (setf (customer-name my-account) "Sally Sue") actually calling the function above?

  • as defined above is setf customer-name a function?

  • in the function above is customer-name in (setf customer-name) and 'customer-name in the body referring to the same thing?

  • the section states

    second element is a symbol, typically the name of a function used to access the place the SETF function will set

    if that's the case then why use the slot-value function inside the function's definition when the function can be used to access the place?

Forefather answered 6/6, 2014 at 21:3 Comment(0)
R
12

In many cases accessing and setting data, one needs two things:

  • a way to retrieve something from a data-structure
  • a way to set something in a data-structure

Thus one would define a setter function and a getter function. For simple cases they also may look simple. But for complex cases they may not. Now if you know the name of the getter, what is the name of the setter? Or: if you know the name of the setter, what is the name of the getter?

Common Lisp has the idea that you only need to know the name of the getter.

  • the getter is called, say, GET-FOO

  • then the setter function is called (SETF GET-FOO). Always.

  • the setter function can be invoked this way: (setf (get-foo some-bar) new-foo). Always.

So you write the GET-FOO function. You also write the (SETF GET-FOO) function and Common Lisp registers it as a setter function.

(SETF GET-FOO) is a list. It's also the name of a function. Here we have the exception: Common Lisp sometimes allows a list as a function name. Thus not all function names are symbols, some are actually lists.

(setf (customer-name my-account) "Sally Sue") is actually a call the the defined setter. my-account is a variable whose value will be bound to the account variable of the setter. "Sally Sue" is a string and it will be bound to the name variable of the setter.

As a developer you only have to know the getter:

  • use of the getter: (customer-name my-account)

  • use of the setter: (setf (customer-name my-account) "Sally Sue"). SETF is a macro, which expands into a call of the setter function.

(defun (setf customer-name) (name account)
  (setf (slot-value account 'customer-name) name))

Above defines a setter function called (setf customer-name).

CL-USER 80 > (function (setf customer-name))
#<interpreted function (SETF CUSTOMER-NAME) 40A00213FC>

When the function gets called via the SETF macro, it calls another setter - this time using the access to a slot value via the slot name.

Ruffle answered 6/6, 2014 at 21:32 Comment(4)
So (setf (customer-name my-account) "Sally Sue") calls (setf-customer-name)? But that function expects two arguments and only one, "Sally Sue", is passed.Forefather
@BleedingFingers, (setf customer-name) is your setter function. Its name not just symbol but a list, so it's not obvious, but when you type (setf (customer-name my-account) "Sally Sue"), you invoke your setter and pass two arguments: string "Sally Sue" and object of bank-account class.Exurb
Where's this specific behavior defined? Had the account been the first argument to (setf customer-name) and name second would it work the same? (Looks some sort of black magic.)Forefather
The semantics of setf functions are defined in the HyperSpec. The new value is always passed as the first argument to the setf function.Broadus
B
2

setf is a very complex macro that knows how to decode its first argument, which usually looks like a function call, as a "place" and then invokes whatever forms are necessary to set the place to the new value. It's not useful to think of (customer-name my-account) as returning anything in the setf expression. The setf macro applies rules defined in the HyperSpec to its place form and, as a default case, will transform

(setf (foo arg0 arg1 ...) new-val)

to

(funcall #'(setf foo) new-val arg0 arg1 ...)

The passage in Practical Common Lisp is explaining, in a somewhat elliptic way, what happens behind the scenes when you specify an :accessor option in a defclass slot definition.

Broadus answered 11/6, 2014 at 13:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.