<#53764#>(set!<#53764#> <#72309#>#tex2html_wrap_inline74260#<#72309#> <#72310#> #tex2html_wrap_inline74262#<#72310#><#53767#>)<#53767#>
<#69406#>A <#69410#><#53782#>set!<#53782#>-expression<#69410#> is an expression that consists of two pieces: a variable and an expression. The variable is fixed; it is never evaluated. The expression is evaluated. In contrast, a structure mutator is a function. As such, it is a value that the program can apply (to two arguments), pass to other functions, store inside of structures, and so on. Structure mutators are created in response to structure definitions, just as structure constructors and selectors. Next we must consider lexical scope issues (see section~#secscope#53783>#tex2html_wrap_inline74264#<#69406#><#53774#>-<#53774#><#72311#> #tex2html_wrap_inline74266#<#72311#><#53776#>-<#53776#><#72312#> #tex2html_wrap_inline74268#<#72312#><#69409#> #tex2html_wrap_inline74270#<#69409#>
<#53792#>(define-struct<#53792#> <#53793#>aaa<#53793#> <#53794#>(xx<#53794#> <#53795#>yy))<#53795#> <#53796#>(d<#53796#><#53797#>efine<#53797#> <#53798#>UNIQUE<#53798#> <#53799#>(l<#53799#><#53800#>ocal<#53800#> <#53801#>(<#53801#><#72375#>the underlined occurrence of <#69413#><#53816#>define-struct<#53816#><#69413#> has a limited lexical scope, and its scope is a hole in the scope of the top-level <#69414#><#53817#>define-struct<#53817#><#69414#>. A result of this scoping is that the mutator for the top-level <#69415#><#53818#>define-struct<#53818#><#69415#> cannot mutate the structure called <#69416#><#53819#>UNIQUE<#53819#><#69416#>. The two mutators are unrelated functions that coincidentally have the same name; the rules for the evaluation of <#53820#>local<#53820#>-expression\ dictate that we rename one consistently. To highlight the differences in syntax and lexical scope, take a look at the following two, apparently similar programs:#tex2html_wrap_inline74272#<#72375#><#53806#>)<#53806#> <#53807#>(make-aaa<#53807#> <#53808#>'<#53808#><#53809#>my<#53809#> <#53810#>'<#53810#><#53811#>world)))<#53811#> <#53812#>...<#53812#>
<#53825#>(define<#53825#> <#53826#>the-point<#53826#> <#53827#>(make-posn<#53827#> <#53828#>3<#53828#> <#53829#>4))<#53829#> <#53830#>(set!<#53830#> <#53831#>x<#53831#> <#53832#>17)<#53832#>
<#53838#>(define<#53838#> <#53839#>the-point<#53839#> <#53840#>(make-posn<#53840#> <#53841#>3<#53841#> <#53842#>4))<#53842#> <#53843#>(set-posn-x!<#53843#> <#53844#>the-point<#53844#> <#53845#>17)<#53845#>The one on the left is illegal, because the <#69417#><#53849#>x<#53849#><#69417#> in the <#53850#>set!<#53850#>-expression\ is an unbound variable. The program on the right is perfectly legal; it refers to the field <#69418#><#53851#>x<#53851#><#69418#> of a <#69419#><#53852#>posn<#53852#><#69419#> structure. The largest difference between <#69420#><#53853#>set!<#53853#>-expression<#69420#>s and mutators concerns their semantics. Let's study two examples to understand the differences once and for all. The first illustrates how similar looking expressions evaluate in a radically different manner:
<#53858#>(define<#53858#> <#53859#>the-point<#53859#> <#53860#>(make-posn<#53860#> <#53861#>3<#53861#> <#53862#>4))<#53862#> <#53863#>(set!<#53863#> <#53864#>the-point<#53864#> <#53865#>17)<#53865#>
<#53871#>(define<#53871#> <#53872#>the-point<#53872#> <#53873#>(make-posn<#53873#> <#53874#>3<#53874#> <#53875#>4))<#53875#> <#53876#>(set-posn-x!<#53876#> <#53877#>the-point<#53877#> <#53878#>17)<#53878#>The program on the left consists of a definition for <#69421#><#53882#>the-point<#53882#><#69421#> and an assignment to <#69422#><#53883#>the-point<#53883#><#69422#>; the one on the right starts with the same definition for <#69423#><#53884#>the-point<#53884#><#69423#> followed by an application of the mutator. The evaluation of both affect the variable definition but in different ways:
<#53889#>(define<#53889#> <#53890#>the-point<#53890#> <#53891#>17)<#53891#> <#53892#>(<#53892#><#53893#>void<#53893#><#53894#>)<#53894#>
<#53900#>(define<#53900#> <#53901#>the-point<#53901#> <#53902#>(make-posn<#53902#> <#53903#>17<#53903#> <#53904#>4))<#53904#> <#53905#>(<#53905#><#53906#>void<#53906#><#53907#>)<#53907#>On the left, <#69424#><#53911#>the-point<#53911#><#69424#> now stands for a number; on the right, it is still a <#69425#><#53912#>posn<#53912#><#69425#> structure but with a new value in the <#69426#><#53913#>x<#53913#><#69426#>-field. More generally, a <#53914#>set!<#53914#>-expression\ changes the value on the right-hand side of a definition, and the application of a mutator changes the value of just one field in a structure that occurs on the right-hand side of a definition. The second example shows how an application of mutator evaluates the arguments, which is not the case for <#69427#><#53915#>set!<#53915#>-expression<#69427#>s:
<#53920#>(define<#53920#> <#53921#>the-point<#53921#> <#53922#>(make-posn<#53922#> <#53923#>3<#53923#> <#53924#>4))<#53924#> <#53925#>(define<#53925#> <#53926#>an-other<#53926#> <#53927#>(make-posn<#53927#> <#53928#>12<#53928#> <#53929#>5))<#53929#> <#53930#>(set!<#53930#> <#53931#>(c<#53931#><#53932#>ond<#53932#> <#53933#>[<#53933#><#53934#>(zero?<#53934#> <#53935#>(point-x<#53935#> <#53936#>the-point))<#53936#> <#53937#>the-point]<#53937#> <#53938#>[<#53938#><#53939#>else<#53939#> <#53940#>an-other]<#53940#><#53941#>)<#53941#> <#53942#>1)<#53942#>
<#53948#>(define<#53948#> <#53949#>the-point<#53949#> <#53950#>(make-posn<#53950#> <#53951#>3<#53951#> <#53952#>4))<#53952#> <#53953#>(define<#53953#> <#53954#>an-other<#53954#> <#53955#>(make-posn<#53955#> <#53956#>12<#53956#> <#53957#>5))<#53957#> <#53958#>(s<#53958#><#53959#>et-posn-x!<#53959#> <#53960#>(c<#53960#><#53961#>ond<#53961#> <#53962#>[<#53962#><#53963#>(zero?<#53963#> <#53964#>(point-x<#53964#> <#53965#>the-point))<#53965#> <#53966#>the-point]<#53966#> <#53967#>[<#53967#><#53968#>else<#53968#> <#53969#>an-other]<#53969#><#53970#>)<#53970#> <#53971#>1)<#53971#>Whereas the program on the left is illegal, because a <#53975#>set!<#53975#>-expression\ must contain a fixed variable in the second position, the one on the right is legitimate. The evaluation of the program on the right changes the <#69428#><#53976#>x<#53976#><#69428#>-field in <#69429#><#53977#>an-other<#53977#><#69429#> to <#69430#><#53978#>1<#53978#><#69430#>. Finally, <#69431#><#53979#>mutators<#53979#><#69431#> are values, which means a function can consume a mutator and apply it:
<#71974#>;; <#69432#><#53984#>set-to-2<#53984#> <#53985#>:<#53985#> <#53986#>S-mutator<#53986#> <#53987#>S-structure<#53987#> <#53988#><#53988#><#53989#>-;SPMgt;<#53989#><#53990#><#53990#> <#53991#>void<#53991#><#69432#><#71974#> <#71975#>;; to change a field in <#69433#><#53992#>s<#53992#><#69433#> to <#69434#><#53993#>2<#53993#><#69434#> via <#69435#><#53994#>mutator<#53994#><#69435#><#71975#> <#53995#>(d<#53995#><#53996#>efine<#53996#> <#53997#>(set-to-2<#53997#> <#53998#>mutator<#53998#> <#53999#>s)<#53999#> <#54000#>(mutator<#54000#> <#54001#>s<#54001#> <#54002#>2))<#54002#> <#54003#>(define-struct<#54003#> <#54004#>bbb<#54004#> <#54005#>(zz<#54005#> <#54006#>ww))<#54006#> <#54007#>(l<#54007#><#54008#>ocal<#54008#> <#54009#>(<#54009#><#54010#>(define<#54010#> <#54011#>s<#54011#> <#54012#>(make-posn<#54012#> <#54013#>3<#54013#> <#54014#>4))<#54014#> <#54015#>(define<#54015#> <#54016#>t<#54016#> <#54017#>(make-bbb<#54017#> <#54018#>'<#54018#><#54019#>a<#54019#> <#54020#>'<#54020#><#54021#>b)))<#54021#> <#54022#>(b<#54022#><#54023#>egin<#54023#> <#54024#>(set-to-2<#54024#> <#54025#>set-posn-x!<#54025#> <#54026#>s)<#54026#> <#54027#>(set-to-2<#54027#> <#54028#>set-bbb-ww!<#54028#> <#54029#>t)))<#54029#>The function <#69436#><#54033#>set-to-2<#54033#><#69436#> consumes a mutator and a structure that the mutator can modify. The program uses it to change the <#69437#><#54034#>x<#54034#><#69437#>-field in a <#69438#><#54035#>posn<#54035#><#69438#> structure and the <#69439#><#54036#>ww<#54036#><#69439#>-field in a <#69440#><#54037#>bbb<#54037#><#69440#> structure. In contrast, if we were to apply a function to a <#54038#>set!<#54038#>-expression, it would receive <#69441#><#54039#>(<#54039#><#54040#>void<#54040#><#54041#>)<#54041#><#69441#> and nothing else.
<#54042#>Mixing set! and Structure Mutators<#54042#>:\
<#54049#>(define<#54049#> <#54050#>the-point<#54050#> <#54051#>(make-posn<#54051#> <#54052#>3<#54052#> <#54053#>4))<#54053#> <#54054#>(define<#54054#> <#54055#>another-point<#54055#> <#54056#>the-point)<#54056#> <#54057#>(b<#54057#><#54058#>egin<#54058#> <#54059#>(set!<#54059#> <#54060#>the-point<#54060#> <#54061#>17)<#54061#> <#54062#>(=<#54062#> <#54063#>(posn-x<#54063#> <#54064#>another-point)<#54064#> <#54065#>3))<#54065#>According to our rules, the two definitions refer to the same structure. The second one does so by indirection. The <#54069#>set!<#54069#>-expression changes what <#69443#><#54070#>the-point<#54070#><#69443#> stands for, but it shouldn't affect the second definition. In particular, the program should produce <#69444#><#54071#>true<#54071#><#69444#>. If we were to use our rules in a naive manner, we would not be able to validate this point. A proper explanation of structures must introduce a new definition for every application of a structure constructor, including those on the right-hand side of definitions in the original program. We will place the new definitions at the beginning of the sequence of definitions. Furthermore, the variable in the new definition must be unique so that it cannot occur in a <#54072#>set!<#54072#>-expression. We will use variables such as <#69445#><#54073#>struct-1<#54073#><#69445#>, <#69446#><#54074#>struct-2<#54074#><#69446#>, and so on and agree to use them for this purpose only. These names, and only these names, are values. Using the minor changes to our rules, we can evaluate the program fragment properly:
<#54079#>;; evaluate from here: <#54079#> <#54080#>(define<#54080#> <#54081#>the-point<#54081#> <#54082#>(make-posn<#54082#> <#54083#>3<#54083#> <#54084#>4))<#54084#> <#54085#>(define<#54085#> <#54086#>another-point<#54086#> <#54087#>the-point)<#54087#> <#54088#>(b<#54088#><#54089#>egin<#54089#> <#54090#>(set!<#54090#> <#54091#>the-point<#54091#> <#54092#>17)<#54092#> <#54093#>(=<#54093#> <#54094#>(posn-x<#54094#> <#54095#>another-point)<#54095#> <#54096#>3))<#54096#> <#54097#>=<#54097#> <#54098#>(define<#54098#> <#54099#>struct-1<#54099#> <#54100#>(make-posn<#54100#> <#54101#>3<#54101#> <#54102#>4))<#54102#> <#54103#>(define<#54103#> <#54104#>the-point<#54104#> <#54105#>struct-1)<#54105#> <#54106#>;; evaluate from here: <#54106#> <#54107#>(define<#54107#> <#54108#>another-point<#54108#> <#54109#>the-point)<#54109#> <#54110#>(b<#54110#><#54111#>egin<#54111#> <#54112#>(set!<#54112#> <#54113#>the-point<#54113#> <#54114#>17)<#54114#> <#54115#>(=<#54115#> <#54116#>(posn-x<#54116#> <#54117#>another-point)<#54117#> <#54118#>3))<#54118#> <#54119#>=<#54119#> <#54120#>(define<#54120#> <#54121#>struct-1<#54121#> <#54122#>(make-posn<#54122#> <#54123#>3<#54123#> <#54124#>4))<#54124#> <#54125#>(define<#54125#> <#54126#>the-point<#54126#> <#54127#>struct-1)<#54127#> <#54128#>(define<#54128#> <#54129#>another-point<#54129#> <#54130#>struct-1)<#54130#> <#54131#>;; evaluate from here: <#54131#> <#54132#>(b<#54132#><#54133#>egin<#54133#> <#54134#>(set!<#54134#> <#54135#>the-point<#54135#> <#54136#>17)<#54136#> <#54137#>(=<#54137#> <#54138#>(posn-x<#54138#> <#54139#>another-point)<#54139#> <#54140#>3))<#54140#>At this point, the structure is created, and both of the original variables refer to the new structure. The rest of the evaluation changes the definition of <#69447#><#54144#>the-point<#54144#><#69447#> but not <#69448#><#54145#>another-point<#54145#><#69448#>:
<#54150#>...<#54150#> <#54151#>=<#54151#> <#54152#>(define<#54152#> <#54153#>struct-1<#54153#> <#54154#>(make-posn<#54154#> <#54155#>3<#54155#> <#54156#>4))<#54156#> <#54157#>(define<#54157#> <#54158#>the-point<#54158#> <#54159#>17)<#54159#> <#54160#>(define<#54160#> <#54161#>another-point<#54161#> <#54162#>struct-1)<#54162#> <#54163#>;; evaluate from here: <#54163#> <#54164#>(b<#54164#><#54165#>egin<#54165#> <#54166#>(<#54166#><#54167#>void<#54167#><#54168#>)<#54168#> <#54169#>(=<#54169#> <#54170#>(posn-x<#54170#> <#54171#>another-point)<#54171#> <#54172#>3))<#54172#> <#54173#>=<#54173#> <#54174#>(define<#54174#> <#54175#>struct-1<#54175#> <#54176#>(make-posn<#54176#> <#54177#>3<#54177#> <#54178#>4))<#54178#> <#54179#>(define<#54179#> <#54180#>the-point<#54180#> <#54181#>17)<#54181#> <#54182#>(define<#54182#> <#54183#>another-point<#54183#> <#54184#>struct-1)<#54184#> <#54185#>;; evaluate from here: <#54185#> <#54186#>(=<#54186#> <#54187#>(posn-x<#54187#> <#54188#>another-point)<#54188#> <#54189#>3)<#54189#> <#54190#>=<#54190#> <#54191#>(define<#54191#> <#54192#>struct-1<#54192#> <#54193#>(make-posn<#54193#> <#54194#>3<#54194#> <#54195#>4))<#54195#> <#54196#>(define<#54196#> <#54197#>the-point<#54197#> <#54198#>17)<#54198#> <#54199#>(define<#54199#> <#54200#>another-point<#54200#> <#54201#>struct-1)<#54201#> <#54202#>;; evaluate from here: <#54202#> <#54203#>(=<#54203#> <#54204#>3<#54204#> <#54205#>3)<#54205#>The final result is <#69449#><#54209#>true<#54209#><#69449#>, as expected. The modified evaluation rules are a bit more cumbersome than the old ones. But, they fully explain the difference between the effects of <#69450#><#54210#>set!<#54210#>-expression<#69450#>s and those of structure mutation, which for programming in modern languages, is an essential concept.~<#69451#><#69451#>