1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986 |
- @c -*-texinfo-*-
- @c This is part of the GNU Guile Reference Manual.
- @c Copyright (C) 2008, 2009, 2011, 2017
- @c Free Software Foundation, Inc.
- @c See the file guile.texi for copying conditions.
- @macro goops
- GOOPS
- @end macro
- @macro guile
- Guile
- @end macro
- @node GOOPS
- @chapter GOOPS
- @goops{} is the object oriented extension to @guile{}. Its
- implementation is derived from @w{STk-3.99.3} by Erick Gallesio and
- version 1.3 of Gregor Kiczales' @cite{Tiny-Clos}. It is very close in
- spirit to CLOS, the Common Lisp Object System, but is adapted for the
- Scheme language.
- @goops{} is a full object oriented system, with classes, objects,
- multiple inheritance, and generic functions with multi-method
- dispatch. Furthermore its implementation relies on a meta object
- protocol --- which means that @goops{}'s core operations are themselves
- defined as methods on relevant classes, and can be customised by
- overriding or redefining those methods.
- To start using @goops{} you first need to import the @code{(oop goops)}
- module. You can do this at the Guile REPL by evaluating:
- @lisp
- (use-modules (oop goops))
- @end lisp
- @findex (oop goops)
- @menu
- * Copyright Notice::
- * Class Definition::
- * Instance Creation::
- * Slot Options::
- * Slot Description Example::
- * Methods and Generic Functions::
- * Inheritance::
- * Introspection::
- * GOOPS Error Handling::
- * GOOPS Object Miscellany::
- * The Metaobject Protocol::
- * Redefining a Class::
- * Changing the Class of an Instance::
- @end menu
- @node Copyright Notice
- @section Copyright Notice
- The material in this chapter is partly derived from the STk Reference
- Manual written by Erick Gallesio, whose copyright notice is as follows.
- Copyright © 1993-1999 Erick Gallesio - I3S-CNRS/ESSI <eg@@unice.fr>
- Permission to use, copy, modify, distribute,and license this
- software and its documentation for any purpose is hereby granted,
- provided that existing copyright notices are retained in all
- copies and that this notice is included verbatim in any
- distributions. No written agreement, license, or royalty fee is
- required for any of the authorized uses.
- This software is provided ``AS IS'' without express or implied
- warranty.
- The material has been adapted for use in Guile, with the author's
- permission.
- @node Class Definition
- @section Class Definition
- A new class is defined with the @code{define-class} syntax:
- @findex define-class
- @cindex class
- @lisp
- (define-class @var{class} (@var{superclass} @dots{})
- @var{slot-description} @dots{}
- @var{class-option} @dots{})
- @end lisp
- @var{class} is the class being defined. The list of @var{superclass}es
- specifies which existing classes, if any, to inherit slots and
- properties from. @dfn{Slots} hold per-instance@footnote{Usually --- but
- see also the @code{#:allocation} slot option.} data, for instances of
- that class --- like ``fields'' or ``member variables'' in other object
- oriented systems. Each @var{slot-description} gives the name of a slot
- and optionally some ``properties'' of this slot; for example its initial
- value, the name of a function which will access its value, and so on.
- Class options, slot descriptions and inheritance are discussed more
- below.
- @cindex slot
- @deffn syntax define-class name (super @dots{}) @
- slot-definition @dots{} class-option @dots{}
- Define a class called @var{name} that inherits from @var{super}s, with
- direct slots defined by @var{slot-definition}s and @var{class-option}s.
- The newly created class is bound to the variable name @var{name} in the
- current environment.
- Each @var{slot-definition} is either a symbol that names the slot or a
- list,
- @example
- (@var{slot-name-symbol} . @var{slot-options})
- @end example
- where @var{slot-name-symbol} is a symbol and @var{slot-options} is a
- list with an even number of elements. The even-numbered elements of
- @var{slot-options} (counting from zero) are slot option keywords; the
- odd-numbered elements are the corresponding values for those keywords.
- Each @var{class-option} is an option keyword and corresponding value.
- @end deffn
- As an example, let us define a type for representing a complex number
- in terms of two real numbers.@footnote{Of course Guile already
- provides complex numbers, and @code{<complex>} is in fact a predefined
- class in GOOPS; but the definition here is still useful as an
- example.} This can be done with the following class definition:
- @lisp
- (define-class <my-complex> (<number>)
- r i)
- @end lisp
- This binds the variable @code{<my-complex>} to a new class whose
- instances will contain two slots. These slots are called @code{r} and
- @code{i} and will hold the real and imaginary parts of a complex
- number. Note that this class inherits from @code{<number>}, which is a
- predefined class.@footnote{@code{<number>} is the direct superclass of
- the predefined class @code{<complex>}; @code{<complex>} is the
- superclass of @code{<real>}, and @code{<real>} is the superclass of
- @code{<integer>}.}
- Slot options are described in the next section. The possible class
- options are as follows.
- @deffn {class option} #:metaclass metaclass
- The @code{#:metaclass} class option specifies the metaclass of the class
- being defined. @var{metaclass} must be a class that inherits from
- @code{<class>}. For the use of metaclasses, see @ref{Metaobjects and
- the Metaobject Protocol} and @ref{Metaclasses}.
- If the @code{#:metaclass} option is absent, GOOPS reuses or constructs a
- metaclass for the new class by calling @code{ensure-metaclass}
- (@pxref{Class Definition Protocol,, ensure-metaclass}).
- @end deffn
- @deffn {class option} #:name name
- The @code{#:name} class option specifies the new class's name. This
- name is used to identify the class whenever related objects - the class
- itself, its instances and its subclasses - are printed.
- If the @code{#:name} option is absent, GOOPS uses the first argument to
- @code{define-class} as the class name.
- @end deffn
- @node Instance Creation
- @section Instance Creation and Slot Access
- An instance (or object) of a defined class can be created with
- @code{make}. @code{make} takes one mandatory parameter, which is the
- class of the instance to create, and a list of optional arguments that
- will be used to initialize the slots of the new instance. For instance
- the following form
- @findex make
- @cindex instance
- @lisp
- (define c (make <my-complex>))
- @end lisp
- @noindent
- creates a new @code{<my-complex>} object and binds it to the Scheme
- variable @code{c}.
- @deffn generic make
- @deffnx method make (class <class>) initarg @dots{}
- Create and return a new instance of class @var{class}, initialized using
- @var{initarg} @enddots{}.
- In theory, @var{initarg} @dots{} can have any structure that is
- understood by whatever methods get applied when the @code{initialize}
- generic function is applied to the newly allocated instance.
- In practice, specialized @code{initialize} methods would normally call
- @code{(next-method)}, and so eventually the standard GOOPS
- @code{initialize} methods are applied. These methods expect
- @var{initargs} to be a list with an even number of elements, where
- even-numbered elements (counting from zero) are keywords and
- odd-numbered elements are the corresponding values.
- GOOPS processes initialization argument keywords automatically for slots
- whose definition includes the @code{#:init-keyword} option (@pxref{Slot
- Options,, init-keyword}). Other keyword value pairs can only be
- processed by an @code{initialize} method that is specialized for the new
- instance's class. Any unprocessed keyword value pairs are ignored.
- @end deffn
- @deffn generic make-instance
- @deffnx method make-instance (class <class>) initarg @dots{}
- @code{make-instance} is an alias for @code{make}.
- @end deffn
- The slots of the new complex number can be accessed using
- @code{slot-ref} and @code{slot-set!}. @code{slot-set!} sets the value
- of an object slot and @code{slot-ref} retrieves it.
- @findex slot-set!
- @findex slot-ref
- @lisp
- @group
- (slot-set! c 'r 10)
- (slot-set! c 'i 3)
- (slot-ref c 'r) @result{} 10
- (slot-ref c 'i) @result{} 3
- @end group
- @end lisp
- The @code{(oop goops describe)} module provides a @code{describe}
- function that is useful for seeing all the slots of an object; it prints
- the slots and their values to standard output.
- @lisp
- (describe c)
- @print{}
- #<<my-complex> 401d8638> is an instance of class <my-complex>
- Slots are:
- r = 10
- i = 3
- @end lisp
- @node Slot Options
- @section Slot Options
- When specifying a slot (in a @code{(define-class @dots{})} form),
- various options can be specified in addition to the slot's name. Each
- option is specified by a keyword. The list of possible keywords is
- as follows.
- @deffn {slot option} #:init-value init-value
- @deffnx {slot option} #:init-form init-form
- @deffnx {slot option} #:init-thunk init-thunk
- @deffnx {slot option} #:init-keyword init-keyword
- These options provide various ways to specify how to initialize the
- slot's value at instance creation time.
- @cindex default slot value
- @var{init-value} specifies a fixed initial slot value (shared across all
- new instances of the class).
- @var{init-thunk} specifies a thunk that will provide a default value for
- the slot. The thunk is called when a new instance is created and should
- return the desired initial slot value.
- @var{init-form} specifies a form that, when evaluated, will return
- an initial value for the slot. The form is evaluated each time that
- an instance of the class is created, in the lexical environment of the
- containing @code{define-class} expression.
- @var{init-keyword} specifies a keyword that can be used to pass an
- initial slot value to @code{make} when creating a new instance.
- Note that, since an @code{init-value} value is shared across all
- instances of a class, you should only use it when the initial value is
- an immutable value, like a constant. If you want to initialize a slot
- with a fresh, independently mutable value, you should use
- @code{init-thunk} or @code{init-form} instead. Consider the following
- example.
- @example
- (define-class <chbouib> ()
- (hashtab #:init-value (make-hash-table)))
- @end example
- @noindent
- Here only one hash table is created and all instances of
- @code{<chbouib>} have their @code{hashtab} slot refer to it. In order
- to have each instance of @code{<chbouib>} refer to a new hash table, you
- should instead write:
- @example
- (define-class <chbouib> ()
- (hashtab #:init-thunk make-hash-table))
- @end example
- @noindent
- or:
- @example
- (define-class <chbouib> ()
- (hashtab #:init-form (make-hash-table)))
- @end example
- If more than one of these options is specified for the same slot, the
- order of precedence, highest first is
- @itemize @bullet
- @item
- @code{#:init-keyword}, if @var{init-keyword} is present in the options
- passed to @code{make}
- @item
- @code{#:init-thunk}, @code{#:init-form} or @code{#:init-value}.
- @end itemize
- If the slot definition contains more than one initialization option of
- the same precedence, the later ones are ignored. If a slot is not
- initialized at all, its value is unbound.
- In general, slots that are shared between more than one instance are
- only initialized at new instance creation time if the slot value is
- unbound at that time. However, if the new instance creation specifies
- a valid init keyword and value for a shared slot, the slot is
- re-initialized regardless of its previous value.
- Note, however, that the power of GOOPS' metaobject protocol means that
- everything written here may be customized or overridden for particular
- classes! The slot initializations described here are performed by the least
- specialized method of the generic function @code{initialize}, whose
- signature is
- @example
- (define-method (initialize (object <object>) initargs) ...)
- @end example
- The initialization of instances of any given class can be customized by
- defining a @code{initialize} method that is specialized for that class,
- and the author of the specialized method may decide to call
- @code{next-method} - which will result in a call to the next less
- specialized @code{initialize} method - at any point within the
- specialized code, or maybe not at all. In general, therefore, the
- initialization mechanisms described here may be modified or overridden by
- more specialized code, or may not be supported at all for particular
- classes.
- @end deffn
- @deffn {slot option} #:getter getter
- @deffnx {slot option} #:setter setter
- @deffnx {slot option} #:accessor accessor
- Given an object @var{obj} with slots named @code{foo} and @code{bar}, it
- is always possible to read and write those slots by calling
- @code{slot-ref} and @code{slot-set!} with the relevant slot name; for
- example:
- @example
- (slot-ref @var{obj} 'foo)
- (slot-set! @var{obj} 'bar 25)
- @end example
- The @code{#:getter}, @code{#:setter} and @code{#:accessor} options, if
- present, tell GOOPS to create generic function and method definitions
- that can be used to get and set the slot value more conveniently.
- @var{getter} specifies a generic function to which GOOPS will add a
- method for getting the slot value. @var{setter} specifies a generic
- function to which GOOPS will add a method for setting the slot value.
- @var{accessor} specifies an accessor to which GOOPS will add methods for
- both getting and setting the slot value.
- So if a class includes a slot definition like this:
- @example
- (c #:getter get-count #:setter set-count #:accessor count)
- @end example
- GOOPS defines generic function methods such that the slot value can be
- referenced using either the getter or the accessor -
- @example
- (let ((current-count (get-count obj))) @dots{})
- (let ((current-count (count obj))) @dots{})
- @end example
- - and set using either the setter or the accessor -
- @example
- (set-count obj (+ 1 current-count))
- (set! (count obj) (+ 1 current-count))
- @end example
- Note that
- @itemize @bullet
- @item
- with an accessor, the slot value is set using the generalized
- @code{set!} syntax
- @item
- in practice, it is unusual for a slot to use all three of these options:
- read-only, write-only and read-write slots would typically use only
- @code{#:getter}, @code{#:setter} and @code{#:accessor} options
- respectively.
- @end itemize
- The binding of the specified names is done in the environment of the
- @code{define-class} expression. If the names are already bound (in that
- environment) to values that cannot be upgraded to generic functions,
- those values are overwritten when the @code{define-class} expression is
- evaluated. For more detail, see @ref{Generic Function Internals,,
- ensure-generic}.
- @end deffn
- @deffn {slot option} #:allocation allocation
- The @code{#:allocation} option tells GOOPS how to allocate storage for
- the slot. Possible values for @var{allocation} are
- @itemize @bullet
- @item @code{#:instance}
- @findex #:instance
- Indicates that GOOPS should create separate storage for this slot in
- each new instance of the containing class (and its subclasses). This is
- the default.
- @item @code{#:class}
- @findex #:class
- Indicates that GOOPS should create storage for this slot that is shared
- by all instances of the containing class (and its subclasses). In other
- words, a slot in class @var{C} with allocation @code{#:class} is shared
- by all @var{instance}s for which @code{(is-a? @var{instance} @var{c})}.
- This permits defining a kind of global variable which can be accessed
- only by (in)direct instances of the class which defines the slot.
- @item @code{#:each-subclass}
- @findex #:each-subclass
- Indicates that GOOPS should create storage for this slot that is shared
- by all @emph{direct} instances of the containing class, and that
- whenever a subclass of the containing class is defined, GOOPS should
- create a new storage for the slot that is shared by all @emph{direct}
- instances of the subclass. In other words, a slot with allocation
- @code{#:each-subclass} is shared by all instances with the same
- @code{class-of}.
- @item @code{#:virtual}
- @findex #:slot-set!
- @findex #:slot-ref
- @findex #:virtual
- Indicates that GOOPS should not allocate storage for this slot. The
- slot definition must also include the @code{#:slot-ref} and
- @code{#:slot-set!} options to specify how to reference and set the value
- for this slot. See the example below.
- @end itemize
- Slot allocation options are processed when defining a new class by the
- generic function @code{compute-get-n-set}, which is specialized by the
- class's metaclass. Hence new types of slot allocation can be
- implemented by defining a new metaclass and a method for
- @code{compute-get-n-set} that is specialized for the new metaclass. For
- an example of how to do this, see @ref{Customizing Class Definition}.
- @end deffn
- @deffn {slot option} #:slot-ref getter
- @deffnx {slot option} #:slot-set! setter
- The @code{#:slot-ref} and @code{#:slot-set!} options must be specified
- if the slot allocation is @code{#:virtual}, and are ignored otherwise.
- @var{getter} should be a closure taking a single @var{instance} parameter
- that returns the current slot value. @var{setter} should be a closure
- taking two parameters - @var{instance} and @var{new-val} - that sets the
- slot value to @var{new-val}.
- @end deffn
- @node Slot Description Example
- @section Illustrating Slot Description
- To illustrate slot description, we can redefine the @code{<my-complex>}
- class seen before. A definition could be:
- @lisp
- (define-class <my-complex> (<number>)
- (r #:init-value 0 #:getter get-r #:setter set-r! #:init-keyword #:r)
- (i #:init-value 0 #:getter get-i #:setter set-i! #:init-keyword #:i))
- @end lisp
- @noindent
- With this definition, the @code{r} and @code{i} slots are set to 0 by
- default, and can be initialised to other values by calling @code{make}
- with the @code{#:r} and @code{#:i} keywords. Also the generic functions
- @code{get-r}, @code{set-r!}, @code{get-i} and @code{set-i!} are
- automatically defined to read and write the slots.
- @lisp
- (define c1 (make <my-complex> #:r 1 #:i 2))
- (get-r c1) @result{} 1
- (set-r! c1 12)
- (get-r c1) @result{} 12
- (define c2 (make <my-complex> #:r 2))
- (get-r c2) @result{} 2
- (get-i c2) @result{} 0
- @end lisp
- Accessors can both read and write a slot. So, another definition of the
- @code{<my-complex>} class, using the @code{#:accessor} option, could be:
- @findex set!
- @lisp
- (define-class <my-complex> (<number>)
- (r #:init-value 0 #:accessor real-part #:init-keyword #:r)
- (i #:init-value 0 #:accessor imag-part #:init-keyword #:i))
- @end lisp
- @noindent
- With this definition, the @code{r} slot can be read with:
- @lisp
- (real-part c)
- @end lisp
- @noindent
- and set with:
- @lisp
- (set! (real-part c) new-value)
- @end lisp
- Suppose now that we want to manipulate complex numbers with both
- rectangular and polar coordinates. One solution could be to have a
- definition of complex numbers which uses one particular representation
- and some conversion functions to pass from one representation to the
- other. A better solution is to use virtual slots, like this:
- @lisp
- (define-class <my-complex> (<number>)
- ;; True slots use rectangular coordinates
- (r #:init-value 0 #:accessor real-part #:init-keyword #:r)
- (i #:init-value 0 #:accessor imag-part #:init-keyword #:i)
- ;; Virtual slots access do the conversion
- (m #:accessor magnitude #:init-keyword #:magn
- #:allocation #:virtual
- #:slot-ref (lambda (o)
- (let ((r (slot-ref o 'r)) (i (slot-ref o 'i)))
- (sqrt (+ (* r r) (* i i)))))
- #:slot-set! (lambda (o m)
- (let ((a (slot-ref o 'a)))
- (slot-set! o 'r (* m (cos a)))
- (slot-set! o 'i (* m (sin a))))))
- (a #:accessor angle #:init-keyword #:angle
- #:allocation #:virtual
- #:slot-ref (lambda (o)
- (atan (slot-ref o 'i) (slot-ref o 'r)))
- #:slot-set! (lambda(o a)
- (let ((m (slot-ref o 'm)))
- (slot-set! o 'r (* m (cos a)))
- (slot-set! o 'i (* m (sin a)))))))
- @end lisp
- In this class definition, the magnitude @code{m} and angle @code{a}
- slots are virtual, and are calculated, when referenced, from the normal
- (i.e.@: @code{#:allocation #:instance}) slots @code{r} and @code{i}, by
- calling the function defined in the relevant @code{#:slot-ref} option.
- Correspondingly, writing @code{m} or @code{a} leads to calling the
- function defined in the @code{#:slot-set!} option. Thus the
- following expression
- @findex #:slot-set!
- @findex #:slot-ref
- @lisp
- (slot-set! c 'a 3)
- @end lisp
- @noindent
- permits to set the angle of the @code{c} complex number.
- @lisp
- (define c (make <my-complex> #:r 12 #:i 20))
- (real-part c) @result{} 12
- (angle c) @result{} 1.03037682652431
- (slot-set! c 'i 10)
- (set! (real-part c) 1)
- (describe c)
- @print{}
- #<<my-complex> 401e9b58> is an instance of class <my-complex>
- Slots are:
- r = 1
- i = 10
- m = 10.0498756211209
- a = 1.47112767430373
- @end lisp
- Since initialization keywords have been defined for the four slots, we
- can now define the standard Scheme primitives @code{make-rectangular}
- and @code{make-polar}.
- @lisp
- (define make-rectangular
- (lambda (x y) (make <my-complex> #:r x #:i y)))
- (define make-polar
- (lambda (x y) (make <my-complex> #:magn x #:angle y)))
- @end lisp
- @node Methods and Generic Functions
- @section Methods and Generic Functions
- A GOOPS method is like a Scheme procedure except that it is specialized
- for a particular set of argument classes, and will only be used when the
- actual arguments in a call match the classes in the method definition.
- @lisp
- (define-method (+ (x <string>) (y <string>))
- (string-append x y))
- (+ "abc" "de") @result{} "abcde"
- @end lisp
- A method is not formally associated with any single class (as it is in
- many other object oriented languages), because a method can be
- specialized for a combination of several classes. If you've studied
- object orientation in non-Lispy languages, you may remember discussions
- such as whether a method to stretch a graphical image around a surface
- should be a method of the image class, with a surface as a parameter, or
- a method of the surface class, with an image as a parameter. In GOOPS
- you'd just write
- @lisp
- (define-method (stretch (im <image>) (sf <surface>))
- ...)
- @end lisp
- @noindent
- and the question of which class the method is more associated with does
- not need answering.
- There can simultaneously be several methods with the same name but
- different sets of specializing argument classes; for example:
- @lisp
- (define-method (+ (x <string>) (y <string)) ...)
- (define-method (+ (x <matrix>) (y <matrix>)) ...)
- (define-method (+ (f <fish>) (b <bicycle>)) ...)
- (define-method (+ (a <foo>) (b <bar>) (c <baz>)) ...)
- @end lisp
- @noindent
- A generic function is a container for the set of such methods that a
- program intends to use.
- If you look at a program's source code, and see @code{(+ x y)} somewhere
- in it, conceptually what is happening is that the program at that point
- calls a generic function (in this case, the generic function bound to
- the identifier @code{+}). When that happens, Guile works out which of
- the generic function's methods is the most appropriate for the arguments
- that the function is being called with; then it evaluates the method's
- code with the arguments as formal parameters. This happens every time
- that a generic function call is evaluated --- it isn't assumed that a
- given source code call will end up invoking the same method every time.
- Defining an identifier as a generic function is done with the
- @code{define-generic} macro. Definition of a new method is done with
- the @code{define-method} macro. Note that @code{define-method}
- automatically does a @code{define-generic} if the identifier concerned
- is not already a generic function, so often an explicit
- @code{define-generic} call is not needed.
- @findex define-generic
- @findex define-method
- @deffn syntax define-generic symbol
- Create a generic function with name @var{symbol} and bind it to the
- variable @var{symbol}. If @var{symbol} was previously bound to a Scheme
- procedure (or procedure-with-setter), the old procedure (and setter) is
- incorporated into the new generic function as its default procedure (and
- setter). Any other previous value, including an existing generic
- function, is discarded and replaced by a new, empty generic function.
- @end deffn
- @deffn syntax define-method (generic parameter @dots{}) body @dots{}
- Define a method for the generic function or accessor @var{generic} with
- parameters @var{parameter}s and body @var{body} @enddots{}.
- @var{generic} is a generic function. If @var{generic} is a variable
- which is not yet bound to a generic function object, the expansion of
- @code{define-method} will include a call to @code{define-generic}. If
- @var{generic} is @code{(setter @var{generic-with-setter})}, where
- @var{generic-with-setter} is a variable which is not yet bound to a
- generic-with-setter object, the expansion will include a call to
- @code{define-accessor}.
- Each @var{parameter} must be either a symbol or a two-element list
- @code{(@var{symbol} @var{class})}. The symbols refer to variables in
- the body forms that will be bound to the parameters supplied by the
- caller when calling this method. The @var{class}es, if present,
- specify the possible combinations of parameters to which this method
- can be applied.
- @var{body} @dots{} are the bodies of the method definition.
- @end deffn
- @code{define-method} expressions look a little like Scheme procedure
- definitions of the form
- @example
- (define (name formals @dots{}) . body)
- @end example
- The important difference is that each formal parameter, apart from the
- possible ``rest'' argument, can be qualified by a class name:
- @code{@var{formal}} becomes @code{(@var{formal} @var{class})}. The
- meaning of this qualification is that the method being defined
- will only be applicable in a particular generic function invocation if
- the corresponding argument is an instance of @code{@var{class}} (or one of
- its subclasses). If more than one of the formal parameters is qualified
- in this way, then the method will only be applicable if each of the
- corresponding arguments is an instance of its respective qualifying class.
- Note that unqualified formal parameters act as though they are qualified
- by the class @code{<top>}, which GOOPS uses to mean the superclass of
- all valid Scheme types, including both primitive types and GOOPS classes.
- For example, if a generic function method is defined with
- @var{parameter}s @code{(s1 <square>)} and @code{(n <number>)}, that
- method is only applicable to invocations of its generic function that
- have two parameters where the first parameter is an instance of the
- @code{<square>} class and the second parameter is a number.
- @menu
- * Accessors::
- * Extending Primitives::
- * Merging Generics::
- * Next-method::
- * Generic Function and Method Examples::
- * Handling Invocation Errors::
- @end menu
- @node Accessors
- @subsection Accessors
- An accessor is a generic function that can also be used with the
- generalized @code{set!} syntax (@pxref{Procedures with Setters}). Guile
- will handle a call like
- @example
- (set! (@code{accessor} @code{args}@dots{}) @code{value})
- @end example
- @noindent
- by calling the most specialized method of @code{accessor} that matches
- the classes of @code{args} and @code{value}. @code{define-accessor} is
- used to bind an identifier to an accessor.
- @deffn syntax define-accessor symbol
- Create an accessor with name @var{symbol} and bind it to the variable
- @var{symbol}. If @var{symbol} was previously bound to a Scheme
- procedure (or procedure-with-setter), the old procedure (and setter) is
- incorporated into the new accessor as its default procedure (and
- setter). Any other previous value, including an existing generic
- function or accessor, is discarded and replaced by a new, empty
- accessor.
- @end deffn
- @node Extending Primitives
- @subsection Extending Primitives
- Many of Guile's primitive procedures can be extended by giving them a
- generic function definition that operates in conjunction with their
- normal C-coded implementation. When a primitive is extended in this
- way, it behaves like a generic function with the C-coded implementation
- as its default method.
- This extension happens automatically if a method is defined (by a
- @code{define-method} call) for a variable whose current value is a
- primitive. But it can also be forced by calling
- @code{enable-primitive-generic!}.
- @deffn {primitive procedure} enable-primitive-generic! primitive
- Force the creation of a generic function definition for
- @var{primitive}.
- @end deffn
- Once the generic function definition for a primitive has been created,
- it can be retrieved using @code{primitive-generic-generic}.
- @deffn {primitive procedure} primitive-generic-generic primitive
- Return the generic function definition of @var{primitive}.
- @code{primitive-generic-generic} raises an error if @var{primitive}
- is not a primitive with generic capability.
- @end deffn
- @node Merging Generics
- @subsection Merging Generics
- GOOPS generic functions and accessors often have short, generic names.
- For example, if a vector package provides an accessor for the X
- coordinate of a vector, that accessor may just be called @code{x}. It
- doesn't need to be called, for example, @code{vector:x}, because
- GOOPS will work out, when it sees code like @code{(x @var{obj})}, that
- the vector-specific method of @code{x} should be called if @var{obj} is
- a vector.
- That raises the question, though, of what happens when different
- packages define a generic function with the same name. Suppose we work
- with a graphical package which needs to use two independent vector
- packages for 2D and 3D vectors respectively. If both packages export
- @code{x}, what does the code using those packages end up with?
- @ref{Creating Guile Modules,,duplicate binding handlers} explains how
- this is resolved for conflicting bindings in general. For generics,
- there is a special duplicates handler, @code{merge-generics}, which
- tells the module system to merge generic functions with the same name.
- Here is an example:
- @lisp
- (define-module (math 2D-vectors)
- #:use-module (oop goops)
- #:export (x y ...))
-
- (define-module (math 3D-vectors)
- #:use-module (oop goops)
- #:export (x y z ...))
- (define-module (my-module)
- #:use-module (oop goops)
- #:use-module (math 2D-vectors)
- #:use-module (math 3D-vectors)
- #:duplicates (merge-generics))
- @end lisp
- The generic function @code{x} in @code{(my-module)} will now incorporate
- all of the methods of @code{x} from both imported modules.
- To be precise, there will now be three distinct generic functions named
- @code{x}: @code{x} in @code{(math 2D-vectors)}, @code{x} in @code{(math
- 3D-vectors)}, and @code{x} in @code{(my-module)}; and these functions
- share their methods in an interesting and dynamic way.
- To explain, let's call the imported generic functions (in @code{(math
- 2D-vectors)} and @code{(math 3D-vectors)}) the @dfn{ancestors}, and the
- merged generic function (in @code{(my-module)}), the @dfn{descendant}.
- The general rule is that for any generic function G, the applicable
- methods are selected from the union of the methods of G's descendant
- functions, the methods of G itself and the methods of G's ancestor
- functions.
- Thus ancestor functions effectively share methods with their
- descendants, and vice versa. In the example above, @code{x} in
- @code{(math 2D-vectors)} will share the methods of @code{x} in
- @code{(my-module)} and vice versa.@footnote{But note that @code{x} in
- @code{(math 2D-vectors)} doesn't share methods with @code{x} in
- @code{(math 3D-vectors)}, so modularity is still preserved.} Sharing is
- dynamic, so adding another new method to a descendant implies adding it
- to that descendant's ancestors too.
- @node Next-method
- @subsection Next-method
- When you call a generic function, with a particular set of arguments,
- GOOPS builds a list of all the methods that are applicable to those
- arguments and orders them by how closely the method definitions match
- the actual argument types. It then calls the method at the top of this
- list. If the selected method's code wants to call on to the next method
- in this list, it can do so by using @code{next-method}.
- @lisp
- (define-method (Test (a <integer>)) (cons 'integer (next-method)))
- (define-method (Test (a <number>)) (cons 'number (next-method)))
- (define-method (Test a) (list 'top))
- @end lisp
- With these definitions,
- @lisp
- (Test 1) @result{} (integer number top)
- (Test 1.0) @result{} (number top)
- (Test #t) @result{} (top)
- @end lisp
- @code{next-method} is always called as just @code{(next-method)}. The
- arguments for the next method call are always implicit, and always the
- same as for the original method call.
- If you want to call on to a method with the same name but with a
- different set of arguments (as you might with overloaded methods in C++,
- for example), you do not use @code{next-method}, but instead simply
- write the new call as usual:
- @lisp
- (define-method (Test (a <number>) min max)
- (if (and (>= a min) (<= a max))
- (display "Number is in range\n"))
- (Test a))
- (Test 2 1 10)
- @print{}
- Number is in range
- @result{}
- (integer number top)
- @end lisp
- (You should be careful in this case that the @code{Test} calls do not
- lead to an infinite recursion, but this consideration is just the same
- as in Scheme code in general.)
- @node Generic Function and Method Examples
- @subsection Generic Function and Method Examples
- Consider the following definitions:
- @lisp
- (define-generic G)
- (define-method (G (a <integer>) b) 'integer)
- (define-method (G (a <real>) b) 'real)
- (define-method (G a b) 'top)
- @end lisp
- The @code{define-generic} call defines @var{G} as a generic function.
- The three next lines define methods for @var{G}. Each method uses a
- sequence of @dfn{parameter specializers} that specify when the given
- method is applicable. A specializer permits to indicate the class a
- parameter must belong to (directly or indirectly) to be applicable. If
- no specializer is given, the system defaults it to @code{<top>}. Thus,
- the first method definition is equivalent to
- @cindex parameter specializers
- @lisp
- (define-method (G (a <integer>) (b <top>)) 'integer)
- @end lisp
- Now, let's look at some possible calls to the generic function @var{G}:
- @lisp
- (G 2 3) @result{} integer
- (G 2 #t) @result{} integer
- (G 1.2 'a) @result{} real
- @c (G #3 'a) @result{} real @c was {\sharpsign}
- (G #t #f) @result{} top
- (G 1 2 3) @result{} error (since no method exists for 3 parameters)
- @end lisp
- The methods above use only one specializer per parameter list. But in
- general, any or all of a method's parameters may be specialized.
- Suppose we define now:
- @lisp
- (define-method (G (a <integer>) (b <number>)) 'integer-number)
- (define-method (G (a <integer>) (b <real>)) 'integer-real)
- (define-method (G (a <integer>) (b <integer>)) 'integer-integer)
- (define-method (G a (b <number>)) 'top-number)
- @end lisp
- @noindent With these definitions:
- @lisp
- (G 1 2) @result{} integer-integer
- (G 1 1.0) @result{} integer-real
- (G 1 #t) @result{} integer
- (G 'a 1) @result{} top-number
- @end lisp
- As a further example we shall continue to define operations on the
- @code{<my-complex>} class. Suppose that we want to use it to implement
- complex numbers completely. For instance a definition for the addition
- of two complex numbers could be
- @lisp
- (define-method (new-+ (a <my-complex>) (b <my-complex>))
- (make-rectangular (+ (real-part a) (real-part b))
- (+ (imag-part a) (imag-part b))))
- @end lisp
- To be sure that the @code{+} used in the method @code{new-+} is the
- standard addition we can do:
- @lisp
- (define-generic new-+)
- (let ((+ +))
- (define-method (new-+ (a <my-complex>) (b <my-complex>))
- (make-rectangular (+ (real-part a) (real-part b))
- (+ (imag-part a) (imag-part b)))))
- @end lisp
- The @code{define-generic} ensures here that @code{new-+} will be defined
- in the global environment. Once this is done, we can add methods to the
- generic function @code{new-+} which make a closure on the @code{+}
- symbol. A complete writing of the @code{new-+} methods is shown in
- @ref{fig:newplus}.
- @float Figure,fig:newplus
- @lisp
- (define-generic new-+)
- (let ((+ +))
- (define-method (new-+ (a <real>) (b <real>)) (+ a b))
- (define-method (new-+ (a <real>) (b <my-complex>))
- (make-rectangular (+ a (real-part b)) (imag-part b)))
- (define-method (new-+ (a <my-complex>) (b <real>))
- (make-rectangular (+ (real-part a) b) (imag-part a)))
- (define-method (new-+ (a <my-complex>) (b <my-complex>))
- (make-rectangular (+ (real-part a) (real-part b))
- (+ (imag-part a) (imag-part b))))
- (define-method (new-+ (a <number>)) a)
-
- (define-method (new-+) 0)
- (define-method (new-+ . args)
- (new-+ (car args)
- (apply new-+ (cdr args)))))
- (set! + new-+)
- @end lisp
- @caption{Extending @code{+} to handle complex numbers}
- @end float
- We take advantage here of the fact that generic function are not obliged
- to have a fixed number of parameters. The four first methods implement
- dyadic addition. The fifth method says that the addition of a single
- element is this element itself. The sixth method says that using the
- addition with no parameter always return 0 (as is also true for the
- primitive @code{+}). The last method takes an arbitrary number of
- parameters@footnote{The parameter list for a @code{define-method}
- follows the conventions used for Scheme procedures. In particular it can
- use the dot notation or a symbol to denote an arbitrary number of
- parameters}. This method acts as a kind of @code{reduce}: it calls the
- dyadic addition on the @emph{car} of the list and on the result of
- applying it on its rest. To finish, the @code{set!} permits to redefine
- the @code{+} symbol to our extended addition.
- To conclude our implementation (integration?) of complex numbers, we
- could redefine standard Scheme predicates in the following manner:
- @lisp
- (define-method (complex? c <my-complex>) #t)
- (define-method (complex? c) #f)
- (define-method (number? n <number>) #t)
- (define-method (number? n) #f)
- @dots{}
- @end lisp
- Standard primitives in which complex numbers are involved could also be
- redefined in the same manner.
- @node Handling Invocation Errors
- @subsection Handling Invocation Errors
- If a generic function is invoked with a combination of parameters for
- which there is no applicable method, GOOPS raises an error.
- @deffn generic no-method
- @deffnx method no-method (gf <generic>) args
- When an application invokes a generic function, and no methods at all
- have been defined for that generic function, GOOPS calls the
- @code{no-method} generic function. The default method calls
- @code{goops-error} with an appropriate message.
- @end deffn
- @deffn generic no-applicable-method
- @deffnx method no-applicable-method (gf <generic>) args
- When an application applies a generic function to a set of arguments,
- and no methods have been defined for those argument types, GOOPS calls
- the @code{no-applicable-method} generic function. The default method
- calls @code{goops-error} with an appropriate message.
- @end deffn
- @deffn generic no-next-method
- @deffnx method no-next-method (gf <generic>) args
- When a generic function method calls @code{(next-method)} to invoke the
- next less specialized method for that generic function, and no less
- specialized methods have been defined for the current generic function
- arguments, GOOPS calls the @code{no-next-method} generic function. The
- default method calls @code{goops-error} with an appropriate message.
- @end deffn
- @node Inheritance
- @section Inheritance
- Here are some class definitions to help illustrate inheritance:
- @lisp
- (define-class A () a)
- (define-class B () b)
- (define-class C () c)
- (define-class D (A B) d a)
- (define-class E (A C) e c)
- (define-class F (D E) f)
- @end lisp
- @code{A}, @code{B}, @code{C} have a null list of superclasses. In this
- case, the system will replace the null list by a list which only
- contains @code{<object>}, the root of all the classes defined by
- @code{define-class}. @code{D}, @code{E}, @code{F} use multiple
- inheritance: each class inherits from two previously defined classes.
- Those class definitions define a hierarchy which is shown in
- @ref{fig:hier}. In this figure, the class @code{<top>} is also shown;
- this class is the superclass of all Scheme objects. In particular,
- @code{<top>} is the superclass of all standard Scheme
- types.
- @float Figure,fig:hier
- @iftex
- @center @image{hierarchy,5in}
- @end iftex
- @ifnottex
- @verbatiminclude hierarchy.txt
- @end ifnottex
- @caption{A class hierarchy.}
- @end float
- When a class has superclasses, its set of slots is calculated by taking
- the union of its own slots and those of all its superclasses. Thus each
- instance of D will have three slots, @code{a}, @code{b} and
- @code{d}). The slots of a class can be discovered using the
- @code{class-slots} primitive. For instance,
- @lisp
- (class-slots A) @result{} ((a))
- (class-slots E) @result{} ((a) (e) (c))
- (class-slots F) @result{} ((e) (c) (b) (d) (a) (f))
- @end lisp
- @noindent
- The ordering of the returned slots is not significant.
- @menu
- * Class Precedence List::
- * Sorting Methods::
- @end menu
- @node Class Precedence List
- @subsection Class Precedence List
- What happens when a class inherits from two or more superclasses that
- have a slot with the same name but incompatible definitions --- for
- example, different init values or slot allocations? We need a rule for
- deciding which slot definition the derived class ends up with, and this
- rule is provided by the class's @dfn{Class Precedence
- List}.@footnote{This section is an adaptation of material from Jeff
- Dalton's (J.Dalton@@ed.ac.uk) @cite{Brief introduction to CLOS}}
- Another problem arises when invoking a generic function, and there is
- more than one method that could apply to the call arguments. Here we
- need a way of ordering the applicable methods, so that Guile knows which
- method to use first, which to use next if that method calls
- @code{next-method}, and so on. One of the ingredients for this ordering
- is determining, for each given call argument, which of the specializing
- classes, from each applicable method's definition, is the most specific
- for that argument; and here again the class precedence list helps.
- If inheritance was restricted such that each class could only have one
- superclass --- which is known as @dfn{single} inheritance --- class
- ordering would be easy. The rule would be simply that a subclass is
- considered more specific than its superclass.
- With multiple inheritance, ordering is less obvious, and we have to
- impose an arbitrary rule to determine precedence. Suppose we have
- @lisp
- (define-class X ()
- (x #:init-value 1))
- (define-class Y ()
- (x #:init-value 2))
- (define-class Z (X Y)
- (@dots{}))
- @end lisp
- @noindent
- Clearly the @code{Z} class is more specific than @code{X} or @code{Y},
- for instances of @code{Z}. But which is more specific out of @code{X}
- and @code{Y} --- and hence, for the definitions above, which
- @code{#:init-value} will take effect when creating an instance of
- @code{Z}? The rule in @goops{} is that the superclasses listed earlier
- are more specific than those listed later. Hence @code{X} is more
- specific than @code{Y}, and the @code{#:init-value} for slot @code{x} in
- instances of @code{Z} will be 1.
- Hence there is a linear ordering for a class and all its
- superclasses, from most specific to least specific, and this ordering is
- called the Class Precedence List of the class.
- In fact the rules above are not quite enough to always determine a
- unique order, but they give an idea of how things work. For example,
- for the @code{F} class shown in @ref{fig:hier}, the class precedence
- list is
- @example
- (f d e a c b <object> <top>)
- @end example
- @noindent
- In cases where there is any ambiguity (like this one), it is a bad idea
- for programmers to rely on exactly what the order is. If the order for
- some superclasses is important, it can be expressed directly in the
- class definition.
- The precedence list of a class can be obtained by calling
- @code{class-precedence-list}. This function returns a ordered list
- whose first element is the most specific class. For instance:
- @lisp
- (class-precedence-list B) @result{} (#<<class> B 401b97c8>
- #<<class> <object> 401e4a10>
- #<<class> <top> 4026a9d8>)
- @end lisp
- @noindent
- Or for a more immediately readable result:
- @lisp
- (map class-name (class-precedence-list B)) @result{} (B <object> <top>)
- @end lisp
- @node Sorting Methods
- @subsection Sorting Methods
- Now, with the idea of the class precedence list, we can state precisely
- how the possible methods are sorted when more than one of the methods of
- a generic function are applicable to the call arguments.
- The rules are that
- @itemize
- @item
- the applicable methods are sorted in order of specificity, and the most
- specific method is used first, then the next if that method calls
- @code{next-method}, and so on
- @item
- a method M1 is more specific than another method M2 if the first
- specializing class that differs, between the definitions of M1 and M2,
- is more specific, in M1's definition, for the corresponding actual call
- argument, than the specializing class in M2's definition
- @item
- a class C1 is more specific than another class C2, for an object of
- actual class C, if C1 comes before C2 in C's class precedence list.
- @end itemize
- @node Introspection
- @section Introspection
- @dfn{Introspection}, or @dfn{reflection}, means being able to obtain
- information dynamically about GOOPS objects. It is perhaps best
- illustrated by considering an object oriented language that does not
- provide any introspection, namely C++.
- Nothing in C++ allows a running program to obtain answers to the following
- types of question:
- @itemize @bullet
- @item
- What are the data members of this object or class?
- @item
- What classes does this class inherit from?
- @item
- Is this method call virtual or non-virtual?
- @item
- If I invoke @code{Employee::adjustHoliday()}, what class contains the
- @code{adjustHoliday()} method that will be applied?
- @end itemize
- In C++, answers to such questions can only be determined by looking at
- the source code, if you have access to it. GOOPS, on the other hand,
- includes procedures that allow answers to these questions --- or their
- GOOPS equivalents --- to be obtained dynamically, at run time.
- @menu
- * Classes::
- * Instances::
- * Slots::
- * Generic Functions::
- * Accessing Slots::
- @end menu
- @node Classes
- @subsection Classes
- A GOOPS class is itself an instance of the @code{<class>} class, or of a
- subclass of @code{<class>}. The definition of the @code{<class>} class
- has slots that are used to describe the properties of a class, including
- the following.
- @deffn {primitive procedure} class-name class
- Return the name of class @var{class}. This is the value of
- @var{class}'s @code{name} slot.
- @end deffn
- @deffn {primitive procedure} class-direct-supers class
- Return a list containing the direct superclasses of @var{class}. This
- is the value of @var{class}'s @code{direct-supers} slot.
- @end deffn
- @deffn {primitive procedure} class-direct-slots class
- Return a list containing the slot definitions of the direct slots of
- @var{class}. This is the value of @var{class}'s @code{direct-slots}
- slot.
- @end deffn
- @deffn {primitive procedure} class-direct-subclasses class
- Return a list containing the direct subclasses of @var{class}. This is
- the value of @var{class}'s @code{direct-subclasses} slot.
- @end deffn
- @deffn {primitive procedure} class-direct-methods class
- Return a list of all the generic function methods that use @var{class}
- as a formal parameter specializer. This is the value of @var{class}'s
- @code{direct-methods} slot.
- @end deffn
- @deffn {primitive procedure} class-precedence-list class
- Return the class precedence list for class @var{class} (@pxref{Class
- Precedence List}). This is the value of @var{class}'s @code{cpl} slot.
- @end deffn
- @deffn {primitive procedure} class-slots class
- Return a list containing the slot definitions for all @var{class}'s
- slots, including any slots that are inherited from superclasses. This
- is the value of @var{class}'s @code{slots} slot.
- @end deffn
- @deffn procedure class-subclasses class
- Return a list of all subclasses of @var{class}.
- @end deffn
- @deffn procedure class-methods class
- Return a list of all methods that use @var{class} or a subclass of
- @var{class} as one of its formal parameter specializers.
- @end deffn
- @node Instances
- @subsection Instances
- @deffn {primitive procedure} class-of value
- Return the GOOPS class of any Scheme @var{value}.
- @end deffn
- @deffn {primitive procedure} instance? object
- Return @code{#t} if @var{object} is any GOOPS instance, otherwise
- @code{#f}.
- @end deffn
- @deffn procedure is-a? object class
- Return @code{#t} if @var{object} is an instance of @var{class} or one of
- its subclasses.
- @end deffn
- You can use the @code{is-a?} predicate to ask whether any given value
- belongs to a given class, or @code{class-of} to discover the class of a
- given value. Note that when GOOPS is loaded (by code using the
- @code{(oop goops)} module) built-in classes like @code{<string>},
- @code{<list>} and @code{<number>} are automatically set up,
- corresponding to all Guile Scheme types.
- @lisp
- (is-a? 2.3 <number>) @result{} #t
- (is-a? 2.3 <real>) @result{} #t
- (is-a? 2.3 <string>) @result{} #f
- (is-a? '("a" "b") <string>) @result{} #f
- (is-a? '("a" "b") <list>) @result{} #t
- (is-a? (car '("a" "b")) <string>) @result{} #t
- (is-a? <string> <class>) @result{} #t
- (is-a? <class> <string>) @result{} #f
- (class-of 2.3) @result{} #<<class> <real> 908c708>
- (class-of #(1 2 3)) @result{} #<<class> <vector> 908cd20>
- (class-of <string>) @result{} #<<class> <class> 8bd3e10>
- (class-of <class>) @result{} #<<class> <class> 8bd3e10>
- @end lisp
- @node Slots
- @subsection Slots
- @deffn procedure class-slot-definition class slot-name
- Return the slot definition for the slot named @var{slot-name} in class
- @var{class}. @var{slot-name} should be a symbol.
- @end deffn
- @deffn procedure slot-definition-name slot-def
- Extract and return the slot name from @var{slot-def}.
- @end deffn
- @deffn procedure slot-definition-options slot-def
- Extract and return the slot options from @var{slot-def}.
- @end deffn
- @deffn procedure slot-definition-allocation slot-def
- Extract and return the slot allocation option from @var{slot-def}. This
- is the value of the @code{#:allocation} keyword (@pxref{Slot Options,,
- allocation}), or @code{#:instance} if the @code{#:allocation} keyword is
- absent.
- @end deffn
- @deffn procedure slot-definition-getter slot-def
- Extract and return the slot getter option from @var{slot-def}. This is
- the value of the @code{#:getter} keyword (@pxref{Slot Options,,
- getter}), or @code{#f} if the @code{#:getter} keyword is absent.
- @end deffn
- @deffn procedure slot-definition-setter slot-def
- Extract and return the slot setter option from @var{slot-def}. This is
- the value of the @code{#:setter} keyword (@pxref{Slot Options,,
- setter}), or @code{#f} if the @code{#:setter} keyword is absent.
- @end deffn
- @deffn procedure slot-definition-accessor slot-def
- Extract and return the slot accessor option from @var{slot-def}. This
- is the value of the @code{#:accessor} keyword (@pxref{Slot Options,,
- accessor}), or @code{#f} if the @code{#:accessor} keyword is absent.
- @end deffn
- @deffn procedure slot-definition-init-value slot-def
- Extract and return the slot init-value option from @var{slot-def}. This
- is the value of the @code{#:init-value} keyword (@pxref{Slot Options,,
- init-value}), or the unbound value if the @code{#:init-value} keyword is
- absent.
- @end deffn
- @deffn procedure slot-definition-init-form slot-def
- Extract and return the slot init-form option from @var{slot-def}. This
- is the value of the @code{#:init-form} keyword (@pxref{Slot Options,,
- init-form}), or the unbound value if the @code{#:init-form} keyword is
- absent.
- @end deffn
- @deffn procedure slot-definition-init-thunk slot-def
- Extract and return the slot init-thunk option from @var{slot-def}. This
- is the value of the @code{#:init-thunk} keyword (@pxref{Slot Options,,
- init-thunk}), or @code{#f} if the @code{#:init-thunk} keyword is absent.
- @end deffn
- @deffn procedure slot-definition-init-keyword slot-def
- Extract and return the slot init-keyword option from @var{slot-def}.
- This is the value of the @code{#:init-keyword} keyword (@pxref{Slot
- Options,, init-keyword}), or @code{#f} if the @code{#:init-keyword}
- keyword is absent.
- @end deffn
- @deffn procedure slot-init-function class slot-name
- Return the initialization function for the slot named @var{slot-name} in
- class @var{class}. @var{slot-name} should be a symbol.
- The returned initialization function incorporates the effects of the
- standard @code{#:init-thunk}, @code{#:init-form} and @code{#:init-value}
- slot options. These initializations can be overridden by the
- @code{#:init-keyword} slot option or by a specialized @code{initialize}
- method, so, in general, the function returned by
- @code{slot-init-function} may be irrelevant. For a fuller discussion,
- see @ref{Slot Options,, init-value}.
- @end deffn
- @node Generic Functions
- @subsection Generic Functions
- A generic function is an instance of the @code{<generic>} class, or of a
- subclass of @code{<generic>}. The definition of the @code{<generic>}
- class has slots that are used to describe the properties of a generic
- function.
- @deffn {primitive procedure} generic-function-name gf
- Return the name of generic function @var{gf}.
- @end deffn
- @deffn {primitive procedure} generic-function-methods gf
- Return a list of the methods of generic function @var{gf}. This is the
- value of @var{gf}'s @code{methods} slot.
- @end deffn
- Similarly, a method is an instance of the @code{<method>} class, or of a
- subclass of @code{<method>}; and the definition of the @code{<method>}
- class has slots that are used to describe the properties of a method.
- @deffn {primitive procedure} method-generic-function method
- Return the generic function that @var{method} belongs to. This is the
- value of @var{method}'s @code{generic-function} slot.
- @end deffn
- @deffn {primitive procedure} method-specializers method
- Return a list of @var{method}'s formal parameter specializers . This is
- the value of @var{method}'s @code{specializers} slot.
- @end deffn
- @deffn {primitive procedure} method-procedure method
- Return the procedure that implements @var{method}. This is the value of
- @var{method}'s @code{procedure} slot.
- @end deffn
- @deffn generic method-source
- @deffnx method method-source (m <method>)
- Return an expression that prints to show the definition of method
- @var{m}.
- @example
- (define-generic cube)
- (define-method (cube (n <number>))
- (* n n n))
- (map method-source (generic-function-methods cube))
- @result{}
- ((method ((n <number>)) (* n n n)))
- @end example
- @end deffn
- @node Accessing Slots
- @subsection Accessing Slots
- Any slot, regardless of its allocation, can be queried, referenced and
- set using the following four primitive procedures.
- @deffn {primitive procedure} slot-exists? obj slot-name
- Return @code{#t} if @var{obj} has a slot with name @var{slot-name},
- otherwise @code{#f}.
- @end deffn
- @deffn {primitive procedure} slot-bound? obj slot-name
- Return @code{#t} if the slot named @var{slot-name} in @var{obj} has a
- value, otherwise @code{#f}.
- @code{slot-bound?} calls the generic function @code{slot-missing} if
- @var{obj} does not have a slot called @var{slot-name} (@pxref{Accessing
- Slots, slot-missing}).
- @end deffn
- @deffn {primitive procedure} slot-ref obj slot-name
- Return the value of the slot named @var{slot-name} in @var{obj}.
- @code{slot-ref} calls the generic function @code{slot-missing} if
- @var{obj} does not have a slot called @var{slot-name} (@pxref{Accessing
- Slots, slot-missing}).
- @code{slot-ref} calls the generic function @code{slot-unbound} if the
- named slot in @var{obj} does not have a value (@pxref{Accessing Slots,
- slot-unbound}).
- @end deffn
- @deffn {primitive procedure} slot-set! obj slot-name value
- Set the value of the slot named @var{slot-name} in @var{obj} to @var{value}.
- @code{slot-set!} calls the generic function @code{slot-missing} if
- @var{obj} does not have a slot called @var{slot-name} (@pxref{Accessing
- Slots, slot-missing}).
- @end deffn
- GOOPS stores information about slots in classes. Internally,
- all of these procedures work by looking up the slot definition for the
- slot named @var{slot-name} in the class @code{(class-of
- @var{obj})}, and then using the slot definition's ``getter'' and
- ``setter'' closures to get and set the slot value.
- The next four procedures differ from the previous ones in that they take
- the class as an explicit argument, rather than assuming
- @code{(class-of @var{obj})}. Therefore they allow you to apply the
- ``getter'' and ``setter'' closures of a slot definition in one class to
- an instance of a different class.
- @deffn {primitive procedure} slot-exists-using-class? class obj slot-name
- Return @code{#t} if @var{class} has a slot definition for a slot with
- name @var{slot-name}, otherwise @code{#f}.
- @end deffn
- @deffn {primitive procedure} slot-bound-using-class? class obj slot-name
- Return @code{#t} if applying @code{slot-ref-using-class} to the same
- arguments would call the generic function @code{slot-unbound}, otherwise
- @code{#f}.
- @code{slot-bound-using-class?} calls the generic function
- @code{slot-missing} if @var{class} does not have a slot definition for a
- slot called @var{slot-name} (@pxref{Accessing Slots,
- slot-missing}).
- @end deffn
- @deffn {primitive procedure} slot-ref-using-class class obj slot-name
- Apply the ``getter'' closure for the slot named @var{slot-name} in
- @var{class} to @var{obj}, and return its result.
- @code{slot-ref-using-class} calls the generic function
- @code{slot-missing} if @var{class} does not have a slot definition for a
- slot called @var{slot-name} (@pxref{Accessing Slots,
- slot-missing}).
- @code{slot-ref-using-class} calls the generic function
- @code{slot-unbound} if the application of the ``getter'' closure to
- @var{obj} returns an unbound value (@pxref{Accessing Slots,
- slot-unbound}).
- @end deffn
- @deffn {primitive procedure} slot-set-using-class! class obj slot-name value
- Apply the ``setter'' closure for the slot named @var{slot-name} in
- @var{class} to @var{obj} and @var{value}.
- @code{slot-set-using-class!} calls the generic function
- @code{slot-missing} if @var{class} does not have a slot definition for a
- slot called @var{slot-name} (@pxref{Accessing Slots, slot-missing}).
- @end deffn
- Slots whose allocation is per-class rather than per-instance can be
- referenced and set without needing to specify any particular instance.
- @deffn procedure class-slot-ref class slot-name
- Return the value of the slot named @var{slot-name} in class @var{class}.
- The named slot must have @code{#:class} or @code{#:each-subclass}
- allocation (@pxref{Slot Options,, allocation}).
- If there is no such slot with @code{#:class} or @code{#:each-subclass}
- allocation, @code{class-slot-ref} calls the @code{slot-missing} generic
- function with arguments @var{class} and @var{slot-name}. Otherwise, if
- the slot value is unbound, @code{class-slot-ref} calls the
- @code{slot-unbound} generic function, with the same arguments.
- @end deffn
- @deffn procedure class-slot-set! class slot-name value
- Set the value of the slot named @var{slot-name} in class @var{class} to
- @var{value}. The named slot must have @code{#:class} or
- @code{#:each-subclass} allocation (@pxref{Slot Options,, allocation}).
- If there is no such slot with @code{#:class} or @code{#:each-subclass}
- allocation, @code{class-slot-ref} calls the @code{slot-missing} generic
- function with arguments @var{class} and @var{slot-name}.
- @end deffn
- When a @code{slot-ref} or @code{slot-set!} call specifies a non-existent
- slot name, or tries to reference a slot whose value is unbound, GOOPS
- calls one of the following generic functions.
- @deffn generic slot-missing
- @deffnx method slot-missing (class <class>) slot-name
- @deffnx method slot-missing (class <class>) (object <object>) slot-name
- @deffnx method slot-missing (class <class>) (object <object>) slot-name value
- When an application attempts to reference or set a class or instance
- slot by name, and the slot name is invalid for the specified @var{class}
- or @var{object}, GOOPS calls the @code{slot-missing} generic function.
- The default methods all call @code{goops-error} with an appropriate
- message.
- @end deffn
- @deffn generic slot-unbound
- @deffnx method slot-unbound (object <object>)
- @deffnx method slot-unbound (class <class>) slot-name
- @deffnx method slot-unbound (class <class>) (object <object>) slot-name
- When an application attempts to reference a class or instance slot, and
- the slot's value is unbound, GOOPS calls the @code{slot-unbound} generic
- function.
- The default methods all call @code{goops-error} with an appropriate
- message.
- @end deffn
- @node GOOPS Error Handling
- @section Error Handling
- The procedure @code{goops-error} is called to raise an appropriate error
- by the default methods of the following generic functions:
- @itemize @bullet
- @item
- @code{slot-missing} (@pxref{Accessing Slots,, slot-missing})
- @item
- @code{slot-unbound} (@pxref{Accessing Slots,, slot-unbound})
- @item
- @code{no-method} (@pxref{Handling Invocation Errors,, no-method})
- @item
- @code{no-applicable-method} (@pxref{Handling Invocation Errors,,
- no-applicable-method})
- @item
- @code{no-next-method} (@pxref{Handling Invocation Errors,,
- no-next-method})
- @end itemize
- If you customize these functions for particular classes or metaclasses,
- you may still want to use @code{goops-error} to signal any error
- conditions that you detect.
- @deffn procedure goops-error format-string arg @dots{}
- Raise an error with key @code{goops-error} and error message constructed
- from @var{format-string} and @var{arg} @enddots{}. Error message
- formatting is as done by @code{scm-error}.
- @end deffn
- @node GOOPS Object Miscellany
- @section GOOPS Object Miscellany
- Here we cover some points about GOOPS objects that aren't substantial
- enough to merit sections on their own.
- @subheading Object Equality
- When GOOPS is loaded, @code{eqv?}, @code{equal?} and @code{=} become
- generic functions, and you can define methods for them, specialized for
- your own classes, so as to control what the various kinds of equality
- mean for your classes.
- For example, the @code{assoc} procedure, for looking up an entry in an
- alist, is specified as using @code{equal?} to determine when the car of
- an entry in the alist is the same as the key parameter that @code{assoc}
- is called with. Hence, if you had defined a new class, and wanted to
- use instances of that class as the keys in an alist, you could define a
- method for @code{equal?}, for your class, to control @code{assoc}'s
- lookup precisely.
- @subheading Cloning Objects
- @deffn generic shallow-clone
- @deffnx method shallow-clone (self <object>)
- Return a ``shallow'' clone of @var{self}. The default method makes a
- shallow clone by allocating a new instance and copying slot values from
- self to the new instance. Each slot value is copied either as an
- immediate value or by reference.
- @end deffn
- @deffn generic deep-clone
- @deffnx method deep-clone (self <object>)
- Return a ``deep'' clone of @var{self}. The default method makes a deep
- clone by allocating a new instance and copying or cloning slot values
- from self to the new instance. If a slot value is an instance
- (satisfies @code{instance?}), it is cloned by calling @code{deep-clone}
- on that value. Other slot values are copied either as immediate values
- or by reference.
- @end deffn
- @subheading Write and Display
- @deffn {primitive generic} write object port
- @deffnx {primitive generic} display object port
- When GOOPS is loaded, @code{write} and @code{display} become generic
- functions with special methods for printing
- @itemize @bullet
- @item
- objects - instances of the class @code{<object>}
- @item
- foreign objects - instances of the class @code{<foreign-object>}
- @item
- classes - instances of the class @code{<class>}
- @item
- generic functions - instances of the class @code{<generic>}
- @item
- methods - instances of the class @code{<method>}.
- @end itemize
- @code{write} and @code{display} print non-GOOPS values in the same way
- as the Guile primitive @code{write} and @code{display} functions.
- @end deffn
- In addition to the cases mentioned, you can of course define
- @code{write} and @code{display} methods for your own classes, to
- customize how instances of those classes are printed.
- @node The Metaobject Protocol
- @section The Metaobject Protocol
- At this point, we've said about as much as can be said about GOOPS
- without having to confront the idea of the metaobject protocol. There
- are a couple more topics that could be discussed in isolation first ---
- class redefinition, and changing the class of existing instances --- but
- in practice developers using them will be advanced enough to want to
- understand the metaobject protocol too, and will probably be using the
- protocol to customize exactly what happens during these events.
- So let's plunge in. GOOPS is based on a ``metaobject protocol'' (aka
- ``MOP'') derived from the ones used in CLOS (the Common Lisp Object
- System), tiny-clos (a small Scheme implementation of a subset of CLOS
- functionality) and STKlos.
- The MOP underlies many possible GOOPS customizations --- such as
- defining an @code{initialize} method to customize the initialization of
- instances of an application-defined class --- and an understanding of
- the MOP makes it much easier to explain such customizations in a precise
- way. And at a deeper level, understanding the MOP is a key part of
- understanding GOOPS, and of taking full advantage of GOOPS' power, by
- customizing the behaviour of GOOPS itself.
- @menu
- * Metaobjects and the Metaobject Protocol::
- * Metaclasses::
- * MOP Specification::
- * Instance Creation Protocol::
- * Class Definition Protocol::
- * Customizing Class Definition::
- * Method Definition::
- * Method Definition Internals::
- * Generic Function Internals::
- * Generic Function Invocation::
- @end menu
- @node Metaobjects and the Metaobject Protocol
- @subsection Metaobjects and the Metaobject Protocol
- The building blocks of GOOPS are classes, slot definitions, instances,
- generic functions and methods. A class is a grouping of inheritance
- relations and slot definitions. An instance is an object with slots
- that are allocated following the rules implied by its class's
- superclasses and slot definitions. A generic function is a collection
- of methods and rules for determining which of those methods to apply
- when the generic function is invoked. A method is a procedure and a set
- of specializers that specify the type of arguments to which the
- procedure is applicable.
- Of these entities, GOOPS represents classes, generic functions and
- methods as ``metaobjects''. In other words, the values in a GOOPS
- program that describe classes, generic functions and methods, are
- themselves instances (or ``objects'') of special GOOPS classes that
- encapsulate the behaviour, respectively, of classes, generic functions,
- and methods.
- (The other two entities are slot definitions and instances. Slot
- definitions are not strictly instances, but every slot definition is
- associated with a GOOPS class that specifies the behaviour of the slot
- as regards accessibility and protection from garbage collection.
- Instances are of course objects in the usual sense, and there is no
- benefit from thinking of them as metaobjects.)
- The ``metaobject protocol'' (or ``MOP'') is the specification of the
- generic functions which determine the behaviour of these metaobjects and
- the circumstances in which these generic functions are invoked.
- For a concrete example of what this means, consider how GOOPS calculates
- the set of slots for a class that is being defined using
- @code{define-class}. The desired set of slots is the union of the new
- class's direct slots and the slots of all its superclasses. But
- @code{define-class} itself does not perform this calculation. Instead,
- there is a method of the @code{initialize} generic function that is
- specialized for instances of type @code{<class>}, and it is this method
- that performs the slot calculation.
- @code{initialize} is a generic function which GOOPS calls whenever a new
- instance is created, immediately after allocating memory for a new
- instance, in order to initialize the new instance's slots. The sequence
- of steps is as follows.
- @itemize @bullet
- @item
- @code{define-class} uses @code{make} to make a new instance of the
- @code{<class>} class, passing as initialization arguments the
- superclasses, slot definitions and class options that were specified in
- the @code{define-class} form.
- @item
- @code{make} allocates memory for the new instance, and invokes the
- @code{initialize} generic function to initialize the new instance's
- slots.
- @item
- The @code{initialize} generic function applies the method that is
- specialized for instances of type @code{<class>}, and this method
- performs the slot calculation.
- @end itemize
- In other words, rather than being hardcoded in @code{define-class}, the
- default behaviour of class definition is encapsulated by generic
- function methods that are specialized for the class @code{<class>}.
- It is possible to create a new class that inherits from @code{<class>},
- which is called a ``metaclass'', and to write a new @code{initialize}
- method that is specialized for instances of the new metaclass. Then, if
- the @code{define-class} form includes a @code{#:metaclass} class option
- whose value is the new metaclass, the class that is defined by the
- @code{define-class} form will be an instance of the new metaclass rather
- than of the default @code{<class>}, and will be defined in accordance
- with the new @code{initialize} method. Thus the default slot
- calculation, as well as any other aspect of the new class's relationship
- with its superclasses, can be modified or overridden.
- In a similar way, the behaviour of generic functions can be modified or
- overridden by creating a new class that inherits from the standard
- generic function class @code{<generic>}, writing appropriate methods
- that are specialized to the new class, and creating new generic
- functions that are instances of the new class.
- The same is true for method metaobjects. And the same basic mechanism
- allows the application class author to write an @code{initialize} method
- that is specialized to their application class, to initialize instances
- of that class.
- Such is the power of the MOP. Note that @code{initialize} is just one
- of a large number of generic functions that can be customized to modify
- the behaviour of application objects and classes and of GOOPS itself.
- Each following section covers a particular area of GOOPS functionality,
- and describes the generic functions that are relevant for customization
- of that area.
- @node Metaclasses
- @subsection Metaclasses
- A @dfn{metaclass} is the class of an object which represents a GOOPS
- class. Put more succinctly, a metaclass is a class's class.
- Most GOOPS classes have the metaclass @code{<class>} and, by default,
- any new class that is created using @code{define-class} has the
- metaclass @code{<class>}.
- But what does this really mean? To find out, let's look in more detail
- at what happens when a new class is created using @code{define-class}:
- @example
- (define-class <my-class> (<object>) . slots)
- @end example
- @noindent
- Guile expands this to something like:
- @example
- (define <my-class> (class (<object>) . slots))
- @end example
- @noindent
- which in turn expands to:
- @example
- (define <my-class>
- (make <class> #:dsupers (list <object>) #:slots slots))
- @end example
- As this expansion makes clear, the resulting value of @code{<my-class>}
- is an instance of the class @code{<class>} with slot values specifying
- the superclasses and slot definitions for the class @code{<my-class>}.
- (@code{#:dsupers} and @code{#:slots} are initialization keywords for the
- @code{dsupers} and @code{dslots} slots of the @code{<class>} class.)
- Now suppose that you want to define a new class with a metaclass other
- than the default @code{<class>}. This is done by writing:
- @example
- (define-class <my-class2> (<object>)
- slot @dots{}
- #:metaclass <my-metaclass>)
- @end example
- @noindent
- and Guile expands @emph{this} to something like:
- @example
- (define <my-class2>
- (make <my-metaclass> #:dsupers (list <object>) #:slots slots))
- @end example
- In this case, the value of @code{<my-class2>} is an instance of the more
- specialized class @code{<my-metaclass>}. Note that
- @code{<my-metaclass>} itself must previously have been defined as a
- subclass of @code{<class>}. For a full discussion of when and how it is
- useful to define new metaclasses, see @ref{MOP Specification}.
- Now let's make an instance of @code{<my-class2>}:
- @example
- (define my-object (make <my-class2> ...))
- @end example
- All of the following statements are correct expressions of the
- relationships between @code{my-object}, @code{<my-class2>},
- @code{<my-metaclass>} and @code{<class>}.
- @itemize @bullet
- @item
- @code{my-object} is an instance of the class @code{<my-class2>}.
- @item
- @code{<my-class2>} is an instance of the class @code{<my-metaclass>}.
- @item
- @code{<my-metaclass>} is an instance of the class @code{<class>}.
- @item
- The class of @code{my-object} is @code{<my-class2>}.
- @item
- The class of @code{<my-class2>} is @code{<my-metaclass>}.
- @item
- The class of @code{<my-metaclass>} is @code{<class>}.
- @end itemize
- @node MOP Specification
- @subsection MOP Specification
- The aim of the MOP specification in this chapter is to specify all the
- customizable generic function invocations that can be made by the standard
- GOOPS syntax, procedures and methods, and to explain the protocol for
- customizing such invocations.
- A generic function invocation is customizable if the types of the
- arguments to which it is applied are not completely determined by the
- lexical context in which the invocation appears. For example, the
- @code{(initialize @var{instance} @var{initargs})} invocation in the
- default @code{make-instance} method is customizable, because the type of
- the @code{@var{instance}} argument is determined by the class that was
- passed to @code{make-instance}.
- (Whereas --- to give a counter-example --- the @code{(make <generic>
- #:name ',name)} invocation in @code{define-generic} is not customizable,
- because all of its arguments have lexically determined types.)
- When using this rule to decide whether a given generic function invocation
- is customizable, we ignore arguments that are expected to be handled in
- method definitions as a single ``rest'' list argument.
- For each customizable generic function invocation, the @dfn{invocation
- protocol} is explained by specifying
- @itemize @bullet
- @item
- what, conceptually, the applied method is intended to do
- @item
- what assumptions, if any, the caller makes about the applied method's side
- effects
- @item
- what the caller expects to get as the applied method's return value.
- @end itemize
- @node Instance Creation Protocol
- @subsection Instance Creation Protocol
- @code{make <class> . @var{initargs}} (method)
- @itemize @bullet
- @item
- @code{allocate-instance @var{class} @var{initargs}} (generic)
- The applied @code{allocate-instance} method should allocate storage for
- a new instance of class @var{class} and return the uninitialized instance.
- @item
- @code{initialize @var{instance} @var{initargs}} (generic)
- @var{instance} is the uninitialized instance returned by
- @code{allocate-instance}. The applied method should initialize the new
- instance in whatever sense is appropriate for its class. The method's
- return value is ignored.
- @end itemize
- @code{make} itself is a generic function. Hence the @code{make}
- invocation itself can be customized in the case where the new instance's
- metaclass is more specialized than the default @code{<class>}, by
- defining a @code{make} method that is specialized to that metaclass.
- Normally, however, the method for classes with metaclass @code{<class>}
- will be applied. This method calls two generic functions:
- @itemize @bullet
- @item
- (allocate-instance @var{class} . @var{initargs})
- @item
- (initialize @var{instance} . @var{initargs})
- @end itemize
- @code{allocate-instance} allocates storage for and returns the new
- instance, uninitialized. You might customize @code{allocate-instance},
- for example, if you wanted to provide a GOOPS wrapper around some other
- object programming system.
- To do this, you would create a specialized metaclass, which would act as
- the metaclass for all classes and instances from the other system. Then
- define an @code{allocate-instance} method, specialized to that
- metaclass, which calls a Guile primitive C function (or FFI code), which
- in turn allocates the new instance using the interface of the other
- object system.
- In this case, for a complete system, you would also need to customize a
- number of other generic functions like @code{make} and
- @code{initialize}, so that GOOPS knows how to make classes from the
- other system, access instance slots, and so on.
- @code{initialize} initializes the instance that is returned by
- @code{allocate-instance}. The standard GOOPS methods perform
- initializations appropriate to the instance class.
- @itemize @bullet
- @item
- At the least specialized level, the method for instances of type
- @code{<object>} performs internal GOOPS instance initialization, and
- initializes the instance's slots according to the slot definitions and
- any slot initialization keywords that appear in @var{initargs}.
- @item
- The method for instances of type @code{<class>} calls
- @code{(next-method)}, then performs the class initializations described
- in @ref{Class Definition Protocol}.
- @item
- and so on for generic functions, methods, operator classes @dots{}
- @end itemize
- Similarly, you can customize the initialization of instances of any
- application-defined class by defining an @code{initialize} method
- specialized to that class.
- Imagine a class whose instances' slots need to be initialized at
- instance creation time by querying a database. Although it might be
- possible to achieve this a combination of @code{#:init-thunk} keywords
- and closures in the slot definitions, it may be neater to write an
- @code{initialize} method for the class that queries the database once
- and initializes all the dependent slot values according to the results.
- @node Class Definition Protocol
- @subsection Class Definition Protocol
- Here is a summary diagram of the syntax, procedures and generic
- functions that may be involved in class definition.
- @noindent
- @code{define-class} (syntax)
- @itemize @bullet
- @item
- @code{class} (syntax)
- @itemize @bullet
- @item
- @code{make-class} (procedure)
- @itemize @bullet
- @item
- @code{ensure-metaclass} (procedure)
- @item
- @code{make @var{metaclass} @dots{}} (generic)
- @itemize @bullet
- @item
- @code{allocate-instance} (generic)
- @item
- @code{initialize} (generic)
- @itemize @bullet
- @item
- @code{compute-cpl} (generic)
- @itemize @bullet
- @item
- @code{compute-std-cpl} (procedure)
- @end itemize
- @item
- @code{compute-slots} (generic)
- @item
- @code{compute-get-n-set} (generic)
- @item
- @code{compute-getter-method} (generic)
- @item
- @code{compute-setter-method} (generic)
- @end itemize
- @end itemize
- @end itemize
- @end itemize
- @item
- @code{class-redefinition} (generic)
- @itemize @bullet
- @item
- @code{remove-class-accessors} (generic)
- @item
- @code{update-direct-method!} (generic)
- @item
- @code{update-direct-subclass!} (generic)
- @end itemize
- @end itemize
- Wherever a step above is marked as ``generic'', it can be customized,
- and the detail shown below it is only ``correct'' insofar as it
- describes what the default method of that generic function does. For
- example, if you write an @code{initialize} method, for some metaclass,
- that does not call @code{next-method} and does not call
- @code{compute-cpl}, then @code{compute-cpl} will not be called when a
- class is defined with that metaclass.
- A @code{(define-class ...)} form (@pxref{Class Definition}) expands to
- an expression which
- @itemize @bullet
- @item
- checks that it is being evaluated only at top level
- @item
- defines any accessors that are implied by the @var{slot-definition}s
- @item
- uses @code{class} to create the new class
- @item
- checks for a previous class definition for @var{name} and, if found,
- handles the redefinition by invoking @code{class-redefinition}
- (@pxref{Redefining a Class}).
- @end itemize
- @deffn syntax class name (super @dots{}) @
- slot-definition @dots{} class-option @dots{}
- Return a newly created class that inherits from @var{super}s, with
- direct slots defined by @var{slot-definition}s and @var{class-option}s.
- For the format of @var{slot-definition}s and @var{class-option}s, see
- @ref{Class Definition,, define-class}.
- @end deffn
- @noindent @code{class} expands to an expression which
- @itemize @bullet
- @item
- processes the class and slot definition options to check that they are
- well-formed, to convert the @code{#:init-form} option to an
- @code{#:init-thunk} option, to supply a default environment parameter
- (the current top-level environment) and to evaluate all the bits that
- need to be evaluated
- @item
- calls @code{make-class} to create the class with the processed and
- evaluated parameters.
- @end itemize
- @deffn procedure make-class supers slots class-option @dots{}
- Return a newly created class that inherits from @var{supers}, with
- direct slots defined by @var{slots} and @var{class-option}s. For the
- format of @var{slots} and @var{class-option}s, see @ref{Class
- Definition,, define-class}, except note that for @code{make-class},
- @var{slots} is a separate list of slot definitions.
- @end deffn
- @noindent @code{make-class}
- @itemize @bullet
- @item
- adds @code{<object>} to the @var{supers} list if @var{supers} is empty
- or if none of the classes in @var{supers} have @code{<object>} in their
- class precedence list
- @item
- defaults the @code{#:environment}, @code{#:name} and @code{#:metaclass}
- options, if they are not specified by @var{options}, to the current
- top-level environment, the unbound value, and @code{(ensure-metaclass
- @var{supers})} respectively
- @item
- checks for duplicate classes in @var{supers} and duplicate slot names in
- @var{slots}, and signals an error if there are any duplicates
- @item
- calls @code{make}, passing the metaclass as the first parameter and all
- other parameters as option keywords with values.
- @end itemize
- @deffn procedure ensure-metaclass supers env
- Return a metaclass suitable for a class that inherits from the list of
- classes in @var{supers}. The returned metaclass is the union by
- inheritance of the metaclasses of the classes in @var{supers}.
- In the simplest case, where all the @var{supers} are straightforward
- classes with metaclass @code{<class>}, the returned metaclass is just
- @code{<class>}.
- For a more complex example, suppose that @var{supers} contained one
- class with metaclass @code{<operator-class>} and one with metaclass
- @code{<foreign-object-class>}. Then the returned metaclass would be a
- class that inherits from both @code{<operator-class>} and
- @code{<foreign-object-class>}.
- If @var{supers} is the empty list, @code{ensure-metaclass} returns the
- default GOOPS metaclass @code{<class>}.
- GOOPS keeps a list of the metaclasses created by
- @code{ensure-metaclass}, so that each required type of metaclass only
- has to be created once.
- The @code{env} parameter is ignored.
- @end deffn
- @deffn generic make metaclass initarg @dots{}
- @var{metaclass} is the metaclass of the class being defined, either
- taken from the @code{#:metaclass} class option or computed by
- @code{ensure-metaclass}. The applied method must create and return the
- fully initialized class metaobject for the new class definition.
- @end deffn
- The @code{(make @var{metaclass} @var{initarg} @dots{})} invocation is a
- particular case of the instance creation protocol covered in the
- previous section. It will create an class metaobject with metaclass
- @var{metaclass}. By default, this metaobject will be initialized by the
- @code{initialize} method that is specialized for instances of type
- @code{<class>}.
- The @code{initialize} method for classes (signature @code{(initialize
- <class> initargs)}) calls the following generic functions.
- @itemize @bullet
- @item
- @code{compute-cpl @var{class}} (generic)
- The applied method should compute and return the class precedence list
- for @var{class} as a list of class metaobjects. When @code{compute-cpl}
- is called, the following @var{class} metaobject slots have all been
- initialized: @code{name}, @code{direct-supers}, @code{direct-slots},
- @code{direct-subclasses} (empty), @code{direct-methods}. The value
- returned by @code{compute-cpl} will be stored in the @code{cpl} slot.
- @item
- @code{compute-slots @var{class}} (generic)
- The applied method should compute and return the slots (union of direct
- and inherited) for @var{class} as a list of slot definitions. When
- @code{compute-slots} is called, all the @var{class} metaobject slots
- mentioned for @code{compute-cpl} have been initialized, plus the
- following: @code{cpl}, @code{redefined} (@code{#f}), @code{environment}.
- The value returned by @code{compute-slots} will be stored in the
- @code{slots} slot.
- @item
- @code{compute-get-n-set @var{class} @var{slot-def}} (generic)
- @code{initialize} calls @code{compute-get-n-set} for each slot computed
- by @code{compute-slots}. The applied method should compute and return a
- pair of closures that, respectively, get and set the value of the specified
- slot. The get closure should have arity 1 and expect a single argument
- that is the instance whose slot value is to be retrieved. The set closure
- should have arity 2 and expect two arguments, where the first argument is
- the instance whose slot value is to be set and the second argument is the
- new value for that slot. The closures should be returned in a two element
- list: @code{(list @var{get} @var{set})}.
- The closures returned by @code{compute-get-n-set} are stored as part of
- the value of the @var{class} metaobject's @code{getters-n-setters} slot.
- Specifically, the value of this slot is a list with the same number of
- elements as there are slots in the class, and each element looks either like
- @example
- @code{(@var{slot-name-symbol} @var{init-function} . @var{index})}
- @end example
- or like
- @example
- @code{(@var{slot-name-symbol} @var{init-function} @var{get} @var{set})}
- @end example
- Where the get and set closures are replaced by @var{index}, the slot is
- an instance slot and @var{index} is the slot's index in the underlying
- structure: GOOPS knows how to get and set the value of such slots and so
- does not need specially constructed get and set closures. Otherwise,
- @var{get} and @var{set} are the closures returned by @code{compute-get-n-set}.
- The structure of the @code{getters-n-setters} slot value is important when
- understanding the next customizable generic functions that @code{initialize}
- calls@dots{}
- @item
- @code{compute-getter-method @var{class} @var{gns}} (generic)
- @code{initialize} calls @code{compute-getter-method} for each of the
- class's slots (as determined by @code{compute-slots}) that includes a
- @code{#:getter} or @code{#:accessor} slot option. @var{gns} is the
- element of the @var{class} metaobject's @code{getters-n-setters} slot
- that specifies how the slot in question is referenced and set, as
- described above under @code{compute-get-n-set}. The applied method
- should create and return a method that is specialized for instances of
- type @var{class} and uses the get closure to retrieve the slot's value.
- @code{initialize} uses @code{add-method!} to add the returned method to
- the generic function named by the slot definition's @code{#:getter} or
- @code{#:accessor} option.
- @item
- @code{compute-setter-method @var{class} @var{gns}} (generic)
- @code{compute-setter-method} is invoked with the same arguments as
- @code{compute-getter-method}, for each of the class's slots that includes
- a @code{#:setter} or @code{#:accessor} slot option. The applied method
- should create and return a method that is specialized for instances of
- type @var{class} and uses the set closure to set the slot's value.
- @code{initialize} then uses @code{add-method!} to add the returned method
- to the generic function named by the slot definition's @code{#:setter}
- or @code{#:accessor} option.
- @end itemize
- @node Customizing Class Definition
- @subsection Customizing Class Definition
- If the metaclass of the new class is something more specialized than the
- default @code{<class>}, then the type of @var{class} in the calls above
- is more specialized than @code{<class>}, and hence it becomes possible
- to define generic function methods, specialized for the new class's
- metaclass, that can modify or override the default behaviour of
- @code{initialize}, @code{compute-cpl} or @code{compute-get-n-set}.
- @code{compute-cpl} computes the class precedence list (``CPL'') for the
- new class (@pxref{Class Precedence List}), and returns it as a list of
- class objects. The CPL is important because it defines a superclass
- ordering that is used, when a generic function is invoked upon an
- instance of the class, to decide which of the available generic function
- methods is the most specific. Hence @code{compute-cpl} could be
- customized in order to modify the CPL ordering algorithm for all classes
- with a special metaclass.
- The default CPL algorithm is encapsulated by the @code{compute-std-cpl}
- procedure, which is called by the default @code{compute-cpl} method.
- @deffn procedure compute-std-cpl class
- Compute and return the class precedence list for @var{class} according
- to the algorithm described in @ref{Class Precedence List}.
- @end deffn
- @code{compute-slots} computes and returns a list of all slot definitions
- for the new class. By default, this list includes the direct slot
- definitions from the @code{define-class} form, plus the slot definitions
- that are inherited from the new class's superclasses. The default
- @code{compute-slots} method uses the CPL computed by @code{compute-cpl}
- to calculate this union of slot definitions, with the rule that slots
- inherited from superclasses are shadowed by direct slots with the same
- name. One possible reason for customizing @code{compute-slots} would be
- to implement an alternative resolution strategy for slot name conflicts.
- @code{compute-get-n-set} computes the low-level closures that will be
- used to get and set the value of a particular slot, and returns them in
- a list with two elements.
- The closures returned depend on how storage for that slot is allocated.
- The standard @code{compute-get-n-set} method, specialized for classes of
- type @code{<class>}, handles the standard GOOPS values for the
- @code{#:allocation} slot option (@pxref{Slot Options,, allocation}). By
- defining a new @code{compute-get-n-set} method for a more specialized
- metaclass, it is possible to support new types of slot allocation.
- Suppose you wanted to create a large number of instances of some class
- with a slot that should be shared between some but not all instances of
- that class - say every 10 instances should share the same slot storage.
- The following example shows how to implement and use a new type of slot
- allocation to do this.
- @example
- (define-class <batched-allocation-metaclass> (<class>))
- (let ((batch-allocation-count 0)
- (batch-get-n-set #f))
- (define-method (compute-get-n-set
- (class <batched-allocation-metaclass>) s)
- (case (slot-definition-allocation s)
- ((#:batched)
- ;; If we've already used the same slot storage for 10 instances,
- ;; reset variables.
- (if (= batch-allocation-count 10)
- (begin
- (set! batch-allocation-count 0)
- (set! batch-get-n-set #f)))
- ;; If we don't have a current pair of get and set closures,
- ;; create one. make-closure-variable returns a pair of closures
- ;; around a single Scheme variable - see goops.scm for details.
- (or batch-get-n-set
- (set! batch-get-n-set (make-closure-variable)))
- ;; Increment the batch allocation count.
- (set! batch-allocation-count (+ batch-allocation-count 1))
- batch-get-n-set)
- ;; Call next-method to handle standard allocation types.
- (else (next-method)))))
- (define-class <class-using-batched-slot> ()
- ...
- (c #:allocation #:batched)
- ...
- #:metaclass <batched-allocation-metaclass>)
- @end example
- The usage of @code{compute-getter-method} and @code{compute-setter-method}
- is described in @ref{Class Definition Protocol}.
- @code{compute-cpl} and @code{compute-get-n-set} are called by the
- standard @code{initialize} method for classes whose metaclass is
- @code{<class>}. But @code{initialize} itself can also be modified, by
- defining an @code{initialize} method specialized to the new class's
- metaclass. Such a method could complete override the standard
- behaviour, by not calling @code{(next-method)} at all, but more
- typically it would perform additional class initialization steps before
- and/or after calling @code{(next-method)} for the standard behaviour.
- @node Method Definition
- @subsection Method Definition
- @code{define-method} (syntax)
- @itemize @bullet
- @item
- @code{add-method! @var{target} @var{method}} (generic)
- @end itemize
- @noindent
- @code{define-method} invokes the @code{add-method!} generic function to
- handle adding the new method to a variety of possible targets. GOOPS
- includes methods to handle @var{target} as
- @itemize @bullet
- @item
- a generic function (the most common case)
- @item
- a procedure
- @item
- a primitive generic (@pxref{Extending Primitives})
- @end itemize
- By defining further methods for @code{add-method!}, you can
- theoretically handle adding methods to further types of target.
- @node Method Definition Internals
- @subsection Method Definition Internals
- @code{define-method}:
- @itemize @bullet
- @item
- checks the form of the first parameter, and applies the following steps
- to the accessor's setter if it has the @code{(setter @dots{})} form
- @item
- interpolates a call to @code{define-generic} or @code{define-accessor}
- if a generic function is not already defined with the supplied name
- @item
- calls @code{method} with the @var{parameter}s and @var{body}, to make a
- new method instance
- @item
- calls @code{add-method!} to add this method to the relevant generic
- function.
- @end itemize
- @deffn syntax method (parameter @dots{}) body @dots{}
- Make a method whose specializers are defined by the classes in
- @var{parameter}s and whose procedure definition is constructed from the
- @var{parameter} symbols and @var{body} forms.
- The @var{parameter} and @var{body} parameters should be as for
- @code{define-method} (@pxref{Methods and Generic Functions,,
- define-method}).
- @end deffn
- @noindent
- @code{method}:
- @itemize @bullet
- @item
- extracts formals and specializing classes from the @var{parameter}s,
- defaulting the class for unspecialized parameters to @code{<top>}
- @item
- creates a closure using the formals and the @var{body} forms
- @item
- calls @code{make} with metaclass @code{<method>} and the specializers
- and closure using the @code{#:specializers} and @code{#:procedure}
- keywords.
- @end itemize
- @deffn procedure make-method specializers procedure
- Make a method using @var{specializers} and @var{procedure}.
- @var{specializers} should be a list of classes that specifies the
- parameter combinations to which this method will be applicable.
- @var{procedure} should be the closure that will applied to the generic
- function parameters when this method is invoked.
- @end deffn
- @noindent
- @code{make-method} is a simple wrapper around @code{make} with metaclass
- @code{<method>}.
- @deffn generic add-method! target method
- Generic function for adding method @var{method} to @var{target}.
- @end deffn
- @deffn method add-method! (generic <generic>) (method <method>)
- Add method @var{method} to the generic function @var{generic}.
- @end deffn
- @deffn method add-method! (proc <procedure>) (method <method>)
- If @var{proc} is a procedure with generic capability (@pxref{Extending
- Primitives,, generic-capability?}), upgrade it to a primitive generic
- and add @var{method} to its generic function definition.
- @end deffn
- @deffn method add-method! (pg <primitive-generic>) (method <method>)
- Add method @var{method} to the generic function definition of @var{pg}.
- Implementation: @code{(add-method! (primitive-generic-generic pg) method)}.
- @end deffn
- @deffn method add-method! (whatever <top>) (method <method>)
- Raise an error indicating that @var{whatever} is not a valid generic
- function.
- @end deffn
- @node Generic Function Internals
- @subsection Generic Function Internals
- @code{define-generic} calls @code{ensure-generic} to upgrade a
- pre-existing procedure value, or @code{make} with metaclass
- @code{<generic>} to create a new generic function.
- @code{define-accessor} calls @code{ensure-accessor} to upgrade a
- pre-existing procedure value, or @code{make-accessor} to create a new
- accessor.
- @deffn procedure ensure-generic old-definition [name]
- Return a generic function with name @var{name}, if possible by using or
- upgrading @var{old-definition}. If unspecified, @var{name} defaults to
- @code{#f}.
- If @var{old-definition} is already a generic function, it is returned
- unchanged.
- If @var{old-definition} is a Scheme procedure or procedure-with-setter,
- @code{ensure-generic} returns a new generic function that uses
- @var{old-definition} for its default procedure and setter.
- Otherwise @code{ensure-generic} returns a new generic function with no
- defaults and no methods.
- @end deffn
- @deffn procedure make-generic [name]
- Return a new generic function with name @code{(car @var{name})}. If
- unspecified, @var{name} defaults to @code{#f}.
- @end deffn
- @code{ensure-generic} calls @code{make} with metaclasses
- @code{<generic>} and @code{<generic-with-setter>}, depending on the
- previous value of the variable that it is trying to upgrade.
- @code{make-generic} is a simple wrapper for @code{make} with metaclass
- @code{<generic>}.
- @deffn procedure ensure-accessor proc [name]
- Return an accessor with name @var{name}, if possible by using or
- upgrading @var{proc}. If unspecified, @var{name} defaults to @code{#f}.
- If @var{proc} is already an accessor, it is returned unchanged.
- If @var{proc} is a Scheme procedure, procedure-with-setter or generic
- function, @code{ensure-accessor} returns an accessor that reuses the
- reusable elements of @var{proc}.
- Otherwise @code{ensure-accessor} returns a new accessor with no defaults
- and no methods.
- @end deffn
- @deffn procedure make-accessor [name]
- Return a new accessor with name @code{(car @var{name})}. If
- unspecified, @var{name} defaults to @code{#f}.
- @end deffn
- @code{ensure-accessor} calls @code{make} with
- metaclass @code{<generic-with-setter>}, as well as calls to
- @code{ensure-generic}, @code{make-accessor} and (tail recursively)
- @code{ensure-accessor}.
- @code{make-accessor} calls @code{make} twice, first
- with metaclass @code{<generic>} to create a generic function for the
- setter, then with metaclass @code{<generic-with-setter>} to create the
- accessor, passing the setter generic function as the value of the
- @code{#:setter} keyword.
- @node Generic Function Invocation
- @subsection Generic Function Invocation
- There is a detailed and customizable protocol involved in the process of
- invoking a generic function --- i.e., in the process of deciding which
- of the generic function's methods are applicable to the current
- arguments, and which one of those to apply. Here is a summary diagram
- of the generic functions involved.
- @noindent
- @code{apply-generic} (generic)
- @itemize @bullet
- @item
- @code{no-method} (generic)
- @item
- @code{compute-applicable-methods} (generic)
- @item
- @code{sort-applicable-methods} (generic)
- @itemize @bullet
- @item
- @code{method-more-specific?} (generic)
- @end itemize
- @item
- @code{apply-methods} (generic)
- @itemize @bullet
- @item
- @code{apply-method} (generic)
- @item
- @code{no-next-method} (generic)
- @end itemize
- @item
- @code{no-applicable-method}
- @end itemize
- We do not yet have full documentation for these. Please refer to the
- code (@file{oop/goops.scm}) for details.
- @node Redefining a Class
- @section Redefining a Class
- Suppose that a class @code{<my-class>} is defined using @code{define-class}
- (@pxref{Class Definition,, define-class}), with slots that have
- accessor functions, and that an application has created several instances
- of @code{<my-class>} using @code{make} (@pxref{Instance Creation,,
- make}). What then happens if @code{<my-class>} is redefined by calling
- @code{define-class} again?
- @menu
- * Redefinable Classes::
- * Default Class Redefinition Behaviour::
- * Customizing Class Redefinition::
- @end menu
- @node Redefinable Classes
- @subsection Redefinable Classes
- The ability for a class to be redefined is a choice for a class author
- to make. By default, classes in GOOPS are @emph{not} redefinable. A
- redefinable class is an instance of @code{<redefinable-class>}; that is
- to say, a class with @code{<redefinable-class>} as its metaclass.
- Accordingly, to define a redefinable class, add @code{#:metaclass
- <redefinable-class>} to its class definition:
- @example
- (define-class <foo> ()
- #:metaclass <redefinable-class>)
- @end example
- Note that any subclass of @code{<foo>} is also redefinable, without the
- need to explicitly pass the @code{#:metaclass} argument, so you only
- need to specify @code{#:metaclass} for the roots of your application's
- class hierarchy.
- @example
- (define-class <bar> (<foo>))
- (class-of <bar>) @result{} <redefinable-class>
- @end example
- Note that prior to Guile 3.0, all GOOPS classes were redefinable in
- theory. In practice, attempting to, for example, redefine
- @code{<class>} itself would almost certainly not do what you want.
- Still, redefinition is an interesting capability when building
- long-lived resilient systems, so GOOPS does offer this facility.
- @node Default Class Redefinition Behaviour
- @subsection Default Class Redefinition Behaviour
- When a class is defined using @code{define-class} and the class name was
- previously defined, by default the new binding just replaces the old
- binding. This is the normal behavior for @code{define}. However if
- both the old and new bindings are redefinable classes (instances of
- @code{<redefinable-class>}), then the class will be updated in place,
- and its instances lazily migrated over.
- The way that the class is updated and the way that the instances migrate
- over are of course part of the meta-object protocol. However the
- default behavior usually suffices, and it goes as follows.
- @itemize @bullet
- @item
- All existing direct instances of @code{<my-class>} are converted to be
- instances of the new class. This is achieved by preserving the values
- of slots that exist in both the old and new definitions, and
- initializing the values of new slots in the usual way (@pxref{Instance
- Creation,, make}).
- @item
- All existing subclasses of @code{<my-class>} are redefined, as though
- the @code{define-class} expressions that defined them were re-evaluated
- following the redefinition of @code{<my-class>}, and the class
- redefinition process described here is applied recursively to the
- redefined subclasses.
- @item
- Once all of its instances and subclasses have been updated, the class
- metaobject previously bound to the variable @code{<my-class>} is no
- longer needed and so can be allowed to be garbage collected.
- @end itemize
- To keep things tidy, GOOPS also needs to do a little housekeeping on
- methods that are associated with the redefined class.
- @itemize @bullet
- @item
- Slot accessor methods for slots in the old definition should be removed
- from their generic functions. They will be replaced by accessor methods
- for the slots of the new class definition.
- @item
- Any generic function method that uses the old @code{<my-class>} metaobject
- as one of its formal parameter specializers must be updated to refer to
- the new @code{<my-class>} metaobject. (Whenever a new generic function
- method is defined, @code{define-method} adds the method to a list stored
- in the class metaobject for each class used as a formal parameter
- specializer, so it is easy to identify all the methods that must be
- updated when a class is redefined.)
- @end itemize
- If this class redefinition strategy strikes you as rather counter-intuitive,
- bear in mind that it is derived from similar behaviour in other object
- systems such as CLOS, and that experience in those systems has shown it to be
- very useful in practice.
- Also bear in mind that, like most of GOOPS' default behaviour, it can
- be customized@dots{}
- @node Customizing Class Redefinition
- @subsection Customizing Class Redefinition
- When @code{define-class} notices that a class is being redefined, it
- constructs the new class metaobject as usual, then invokes the
- @code{class-redefinition} generic function with the old and new classes
- as arguments. Therefore, if the old or new classes have metaclasses
- other than the default @code{<redefinable-class>}, class redefinition
- behaviour can be customized by defining a @code{class-redefinition}
- method that is specialized for the relevant metaclasses.
- @deffn generic class-redefinition
- Handle the class redefinition from @var{old} to @var{new}, and return
- the new class metaobject that should be bound to the variable specified
- by @code{define-class}'s first argument.
- @end deffn
- @deffn method class-redefinition (old <top>) (new <class>)
- Not all classes are redefinable, and not all previous bindings are
- classes. @xref{Redefinable Classes}. This default method just returns
- @var{new}.
- @end deffn
- @deffn method class-redefinition (old <redefinable-class>) (new <redefinable-class>)
- This method implements GOOPS' default class redefinition behaviour, as
- described in @ref{Default Class Redefinition Behaviour}. Returns the
- metaobject for the new class definition.
- @end deffn
- The @code{class-redefinition} method for classes with metaclass
- @code{<redefinable-class>} calls the following generic functions, which
- could of course be individually customized.
- @deffn generic remove-class-accessors! old
- The default @code{remove-class-accessors!} method removes the accessor
- methods of the old class from all classes which they specialize.
- @end deffn
- @deffn generic update-direct-method! method old new
- The default @code{update-direct-method!} method substitutes the new
- class for the old in all methods specialized to the old class.
- @end deffn
- @deffn generic update-direct-subclass! subclass old new
- The default @code{update-direct-subclass!} method invokes
- @code{class-redefinition} recursively to handle the redefinition of
- subclasses.
- @end deffn
- An alternative class redefinition strategy could be to leave all
- existing instances as instances of the old class, but accepting that the
- old class is now ``nameless'', since its name has been taken over by the
- new definition. In this strategy, any existing subclasses could also
- be left as they are, on the understanding that they inherit from a nameless
- superclass.
- This strategy is easily implemented in GOOPS, by defining a new metaclass,
- that will be used as the metaclass for all classes to which the strategy
- should apply, and then defining a @code{class-redefinition} method that
- is specialized for this metaclass:
- @example
- (define-class <can-be-nameless> (<redefinable-class>))
- (define-method (class-redefinition (old <can-be-nameless>)
- (new <class>))
- new)
- @end example
- When customization can be as easy as this, aren't you glad that GOOPS
- implements the far more difficult strategy as its default!
- @node Changing the Class of an Instance
- @section Changing the Class of an Instance
- When a redefinable class is redefined, any existing instance of the
- redefined class will be modified for the new class definition before the
- next time that any of the instance's slots is referenced or set. GOOPS
- modifies each instance by calling the generic function
- @code{change-class}.
- More generally, you can change the class of an existing instance at any
- time by invoking the generic function @code{change-class} with two
- arguments: the instance and the new class.
- The default method for @code{change-class} decides how to implement the
- change of class by looking at the slot definitions for the instance's
- existing class and for the new class. If the new class has slots with
- the same name as slots in the existing class, the values for those slots
- are preserved. Slots that are present only in the existing class are
- discarded. Slots that are present only in the new class are initialized
- using the corresponding slot definition's init function (@pxref{Classes,,
- slot-init-function}).
- @deffn generic change-class instance new-class
- @end deffn
- @deffn {method} change-class (obj <object>) (new <redefinable-class>)
- Modify instance @var{obj} to make it an instance of class @var{new}.
- @var{obj} itself must already be an instance of a redefinable class.
- The value of each of @var{obj}'s slots is preserved only if a similarly named
- slot exists in @var{new}; any other slot values are discarded.
- The slots in @var{new} that do not correspond to any of @var{obj}'s
- pre-existing slots are initialized according to @var{new}'s slot definitions'
- init functions.
- @end deffn
- The default @code{change-class} method also invokes another generic
- function, @code{update-instance-for-different-class}, as the last thing
- that it does before returning. The applied
- @code{update-instance-for-different-class} method can make any further
- adjustments to @var{new-instance} that are required to complete or
- modify the change of class. The return value from the applied method is
- ignored.
- @deffn generic update-instance-for-different-class old-instance new-instance
- A generic function that can be customized to put finishing touches to an
- instance whose class has just been changed. The default
- @code{update-instance-for-different-class} method does nothing.
- @end deffn
- Customized change of class behaviour can be implemented by defining
- @code{change-class} methods that are specialized either by the class
- of the instances to be modified or by the metaclass of the new class.
|