Abstracting with State Variables

Suppose we wish to turn the program in figure~#figsvrecipe1example#50584> (page~#figsvrecipe1example#50585>) into a program for managing (simulated) traffic lights. A traffic light comes with a single state variable, <#69048#><#50586#>current-color<#50586#><#69048#>, and a single service, <#69049#><#50587#>next<#50587#><#69049#>, which switches the traffic light to the next color according to the traffic laws. For a graphical simulation, the latter would also re-draw the traffic light so that users can view the current color. In a simulation, each traffic light has a unique location on the canvas:
rawhtml62 A user should be able to control each traffic light independently of the others. Indeed, the operator should be able to add or shut down traffic lights while the rest of the system remains unchanged and running. The sample program of figure~#figsvrecipe1example#50588> deals with a single traffic light, though without the drawing operation. The problem is now to turn this into a program that can create as many traffic lights as needed, each with its own state variables and switching functions and at its own location. If we were to copy the definitions in figure~#figsvrecipe1example#50589> and add definitions for dealing with the canvas, the various instances would differ in only one aspect: the data concerning the location of the traffic light. This thought experiment suggests that we should develop an abstract function for creates and manages traffic lights at various locations. Because the original program consists of several top-level definitions, we use the recipe of section~#secdesignabs1st#50590>, which suggests to wrap the definitions in a <#69050#><#50591#>local<#50591#>-expression<#69050#> inside a function. When local definitions include state variables, as in this example, we prefer to say that we <#69051#><#50592#>ENCAPSULATE<#50592#><#69051#> definitions. This terminology emphasizes that the abstract function hides the state variables from other parts of the program, and deemphasizes the act of abstraction. Still, the definition encapsulates and abstracts at the same time, and a programmer must keep this in mind.
<#69053#>;; <#50598#>View:<#50598#><#69053#>
<#71920#>;; <#69054#><#50599#>draw-light<#50599#> <#50600#>:<#50600#> <#50601#>TL-color<#50601#> <#50602#>number<#50602#> <#50603#><#50603#><#50604#>-;SPMgt;<#50604#><#50605#><#50605#> <#50606#>true<#50606#><#69054#><#71920#> 
<#50607#>;; to (re)draw the traffic light on the canvas <#50607#> 
<#50608#>(define<#50608#> <#50609#>(draw-light<#50609#> <#50610#>current-color<#50610#> <#50611#>x-posn)<#50611#> <#50612#>...))<#50612#> 
<#69055#>;; <#50613#>Model:<#50613#><#69055#> 
<#71921#>;; <#69056#><#50614#>make-traffic-light<#50614#> <#50615#>:<#50615#> <#50616#>symbol<#50616#> <#50617#>number<#50617#> <#50618#><#50618#><#50619#>-;SPMgt;<#50619#><#50620#><#50620#> <#50621#>(<#50621#><#50622#><#50622#><#50623#>-;SPMgt;<#50623#><#50624#><#50624#> <#50625#>true)<#50625#><#69056#><#71921#> 
<#71922#>;; to create a red light with <#69057#><#50626#>(make-posn<#50626#> <#50627#>x-posn<#50627#> <#50628#>0)<#50628#><#69057#> as the upper-left corner<#71922#> 
<#50629#>;; effect: draw the traffic light on the canvas<#50629#> 
<#50630#>(d<#50630#><#50631#>efine<#50631#> <#50632#>(make-traffic-light<#50632#> <#50633#>street<#50633#> <#50634#>x-posn)<#50634#> 
  <#50635#>(l<#50635#><#50636#>ocal<#50636#> <#50637#>(<#50637#><#71923#>;; <#69058#><#50638#>current-color<#50638#> <#50639#>:<#50639#> <#50640#>TL-color<#50640#><#69058#><#71923#> 
          <#50641#>;; to keep track of the current color of the traffic light<#50641#> 
          <#50642#>(define<#50642#> <#50643#>current-color<#50643#> <#50644#>'<#50644#><#50645#>red)<#50645#> 
          
          <#71924#>;; <#69059#><#50646#>init-traffic-light<#50646#> <#50647#>:<#50647#> <#50648#><#50648#><#50649#>-;SPMgt;<#50649#><#50650#><#50650#> <#50651#>true<#50651#><#69059#><#71924#> 
          <#71925#>;; to (re)set <#69060#><#50652#>current-color<#50652#><#69060#> to red and to (re)create the view <#71925#> 
          <#50653#>(d<#50653#><#50654#>efine<#50654#> <#50655#>(init-traffic-light)<#50655#> 
            <#50656#>(b<#50656#><#50657#>egin<#50657#> 
              <#50658#>(set!<#50658#> <#50659#>current-color<#50659#> <#50660#>'<#50660#><#50661#>red)<#50661#> 
              <#50662#>(draw-light<#50662#> <#50663#>current-color<#50663#> <#50664#>x-posn)))<#50664#> 
          
          <#71926#>;; <#69061#><#50665#>next<#50665#> <#50666#>:<#50666#> <#50667#><#50667#><#50668#>-;SPMgt;<#50668#><#50669#><#50669#> <#50670#>true<#50670#><#69061#><#71926#> 
          <#71927#>;; effect: to change <#69062#><#50671#>current-color<#50671#><#69062#> from <#69063#><#50672#>'<#50672#><#50673#>green<#50673#><#69063#> to <#69064#><#50674#>'<#50674#><#50675#>yellow<#50675#><#69064#>, <#71927#> 
          <#71928#>;; <#69065#><#50676#>'<#50676#><#50677#>yellow<#50677#><#69065#> to <#69066#><#50678#>'<#50678#><#50679#>red<#50679#><#69066#>, and <#69067#><#50680#>'<#50680#><#50681#>red<#50681#><#69067#> to <#69068#><#50682#>'<#50682#><#50683#>green<#50683#><#69068#><#71928#> 
          <#50684#>(d<#50684#><#50685#>efine<#50685#> <#50686#>(next)<#50686#> 
            <#50687#>(b<#50687#><#50688#>egin<#50688#> 
              <#50689#>(set!<#50689#> <#50690#>current-color<#50690#> <#50691#>(next-color<#50691#> <#50692#>current-color))<#50692#> 
              <#50693#>(draw-light<#50693#> <#50694#>current-color<#50694#> <#50695#>x-posn)))<#50695#> 
          
          <#71929#>;; <#69069#><#50696#>next-color<#50696#> <#50697#>:<#50697#> <#50698#>TL-color<#50698#> <#50699#><#50699#><#50700#>-;SPMgt;<#50700#><#50701#><#50701#> <#50702#>TL-color<#50702#><#69069#><#71929#> 
          <#71930#>;; to compute the successor of <#69070#><#50703#>current-color<#50703#><#69070#> based on the traffic laws<#71930#> 
          <#50704#>(d<#50704#><#50705#>efine<#50705#> <#50706#>(next-color<#50706#> <#50707#>current-color)<#50707#> 
            <#50708#>(c<#50708#><#50709#>ond<#50709#> 
              <#50710#>[<#50710#><#50711#>(symbol=?<#50711#> <#50712#>'<#50712#><#50713#>green<#50713#> <#50714#>current-color)<#50714#> <#50715#>'<#50715#><#50716#>yellow]<#50716#> 
              <#50717#>[<#50717#><#50718#>(symbol=?<#50718#> <#50719#>'<#50719#><#50720#>yellow<#50720#> <#50721#>current-color)<#50721#> <#50722#>'<#50722#><#50723#>red]<#50723#> 
              <#50724#>[<#50724#><#50725#>(symbol=?<#50725#> <#50726#>'<#50726#><#50727#>red<#50727#> <#50728#>current-color)<#50728#> <#50729#>'<#50729#><#50730#>green]<#50730#><#50731#>))<#50731#> 
    <#50732#>(b<#50732#><#50733#>egin<#50733#> 
      <#71931#>;; Initialize and produce <#69071#><#50734#>next<#50734#><#69071#><#71931#> 
      <#50735#>(init-traffic-light)<#50735#> 
      <#50736#>next)))<#50736#> 
<#50740#>Figure: Managing multiple traffic lights<#50740#>
The next step is to consider what this function should do, that is, what it should consume, what it should produce, and what effects it should have, if any. Let's start with the name. We call the new function <#69072#><#50742#>make-traffic-light<#50742#><#69072#>; after all, making a simulated traffic light is the purpose of the abstracted program. Furthermore, according to our abstraction recipes, an abstraction must consume values that represent the unique aspects of an instance. The unique aspect of a traffic light is its position on the canvas; for clarity, let's add a physical address, too. Every use of <#69073#><#50743#>make-traffic-light<#50743#><#69073#> should create a traffic light and enable the operator to switch it from one state to the next. The first part suggests an effect. Specifically, the function should initialize the state variable and draw the initial state of the traffic light at the designated position on the canvas. The second part of the statement suggests a result: a function for switching the state of the traffic light. Figure~#figmultipletl#50744> contains the outline of the traffic simulator, including the complete definition of <#69074#><#50745#>make-traffic-light<#50745#><#69074#>. The simulator consists of a model and a view. The model is <#69075#><#50746#>make-traffic-light<#50746#><#69075#>. The view is called <#69076#><#50747#>draw-light<#50747#><#69076#> and is only sketched; the full definition of the view is left as an exercise. The definition of <#69077#><#50748#>make-traffic-light<#50748#><#69077#> is an ordinary function definition. It uses a <#69078#><#50749#>local<#50749#><#69078#> definition to set up the single state variable, the initializer, and the state-changing function. The body of the <#69079#><#50750#>local<#50750#>-expression<#69079#> uses the initializer and then produces <#69080#><#50751#>next<#50751#><#69080#> as the function's value. Using <#69081#><#50752#>make-traffic-light<#50752#><#69081#> we can create svereal individual traffic lights or entire collections of them. We could also add lights as time goes by. First, we create a sufficient large canvas:
<#50757#>;; create the canvas first <#50757#>
<#50758#>(start<#50758#> <#50759#>300<#50759#> <#50760#>160)<#50760#> 
Second, we apply <#69082#><#50764#>make-traffic-light<#50764#><#69082#> as often as needed:
<#71932#>;; <#69083#><#50769#>lights<#50769#> <#50770#>:<#50770#> <#50771#>(listof<#50771#> <#50772#>traffic-light)<#50772#><#69083#><#71932#>
<#50773#>;; to manage the lights along Sunrise <#50773#> 
<#50774#>(d<#50774#><#50775#>efine<#50775#> <#50776#>lights<#50776#> 
  <#50777#>(list<#50777#> <#50778#>(make-traffic-light<#50778#> <#50779#>'<#50779#><#50780#>sunrise@<#50780#><#50781#>rice<#50781#> <#50782#>50)<#50782#> 
        <#50783#>(make-traffic-light<#50783#> <#50784#>'<#50784#><#50785#>sunrise@<#50785#><#50786#>cmu<#50786#> <#50787#>150)))<#50787#> 
Here we define <#69084#><#50791#>lights<#50791#><#69084#> to be a list of two traffic lights. Each traffic light is a function, so <#69085#><#50792#>lights<#50792#><#69085#> stands for a list of two functions. After creating the traffic lights, we can change their states as desired. To do so, we must keep in mind that each traffic light is represented by a function that consumes nothing and produces <#69086#><#50793#>true<#50793#><#69086#>. Its effect is to change the hidden state variable and the drawing on the canvas. In our running example, we could use the <#50794#>Interactions<#50794#> window as follows:
<#50799#>;SPMgt;<#50799#> <#50800#>((second<#50800#> <#50801#>lights))<#50801#> 
<#50802#>true<#50802#> 
<#50803#>;SPMgt;<#50803#> <#50804#>(andmap<#50804#> <#50805#>(lambda<#50805#> <#50806#>(a-light)<#50806#> <#50807#>(a-light))<#50807#> <#50808#>lights)<#50808#> 
<#50809#>true<#50809#> 
The first interaction extracts the second item from <#69087#><#50813#>lights<#50813#><#69087#> and applies it. This sets the light at <#69088#><#50814#>'<#50814#><#50815#>sunrise@<#50815#><#50816#>cmu<#50816#><#69088#> to red. The second one switches the state of all items on <#69089#><#50817#>lights<#50817#><#69089#>. Each application of <#69090#><#50818#>make-traffic-light<#50818#><#69090#> turns variants of the <#69091#><#50819#>local<#50819#><#69091#> definitions into top-level definitions, after renaming them. Because the above <#69092#><#50820#>define<#50820#><#69092#> contains two applications of <#69093#><#50821#>make-traffic-light<#50821#><#69093#>, it creates two copies of each <#69094#><#50822#>local<#50822#><#69094#>ly defined function and state variable during an evaluation:
<#71933#>;; definitions for <#69095#><#50827#>'<#50827#><#50828#>sunrise@<#50828#><#50829#>rice<#50829#><#69095#><#71933#>
<#50830#>(define<#50830#> <#50831#>current-color@<#50831#><#50832#>rice<#50832#> <#50833#>'<#50833#><#50834#>red)<#50834#> 
<#50835#>(d<#50835#><#50836#>efine<#50836#> <#50837#>(init-traffic-light@<#50837#><#50838#>rice)<#50838#> 
  <#50839#>(b<#50839#><#50840#>egin<#50840#> 
    <#50841#>(set!<#50841#> <#50842#>current-color@<#50842#><#50843#>rice<#50843#> <#50844#>'<#50844#><#50845#>red)<#50845#> 
    <#50846#>(draw-light<#50846#> <#50847#>current-color@<#50847#><#50848#>rice<#50848#> <#50849#>50)))<#50849#> 
<#50850#>(define<#50850#> <#50851#>(next@<#50851#><#50852#>rice)<#50852#> <#50853#>...)<#50853#> 
<#50854#>(define<#50854#> <#50855#>(next-color@<#50855#><#50856#>rice<#50856#> <#50857#>current-color)<#50857#> <#50858#>...)<#50858#> 
<#71934#>;; definitions for <#69096#><#50859#>'<#50859#><#50860#>sunrise@<#50860#><#50861#>cmu<#50861#><#69096#><#71934#> 
<#50862#>(define<#50862#> <#50863#>current-color@<#50863#><#50864#>cmu<#50864#> <#50865#>'<#50865#><#50866#>red)<#50866#> 
<#50867#>(d<#50867#><#50868#>efine<#50868#> <#50869#>(init-traffic-light@<#50869#><#50870#>cmu)<#50870#> 
  <#50871#>(b<#50871#><#50872#>egin<#50872#> 
    <#50873#>(set!<#50873#> <#50874#>current-color@<#50874#><#50875#>cmu<#50875#> <#50876#>'<#50876#><#50877#>red)<#50877#> 
    <#50878#>(draw-light<#50878#> <#50879#>current-color@<#50879#><#50880#>cmu<#50880#> <#50881#>150)))<#50881#> 
<#50882#>(define<#50882#> <#50883#>(next@<#50883#><#50884#>cmu)<#50884#> <#50885#>...)<#50885#> 
<#50886#>(define<#50886#> <#50887#>(next-color@<#50887#><#50888#>cmu<#50888#> <#50889#>current-color)<#50889#> <#50890#>...)<#50890#> 
<#50891#>(d<#50891#><#50892#>efine<#50892#> <#50893#>lights<#50893#> 
  <#50894#>(list<#50894#> <#50895#>next@<#50895#><#50896#>rice<#50896#> 
        <#50897#>next@<#50897#><#50898#>cmu))<#50898#> 
The new top-level definitions of <#69097#><#50902#>init-traffic-light<#50902#><#69097#> show how the renaming ensures that one of them takes care of <#69098#><#50903#>'<#50903#><#50904#>sunrise@<#50904#><#50905#>rice<#50905#><#69098#> and the other one of <#69099#><#50906#>'<#50906#><#50907#>sunrise@<#50907#><#50908#>cmu<#50908#><#69099#>.
<#50911#>Exercise 39.1.1<#50911#> What is the concrete effect of the second interaction above?~ external Solution<#69100#><#69100#> <#50918#>Exercise 39.1.2<#50918#> Fill in the bodies of <#69101#><#50920#>next@<#50920#><#50921#>rice<#50921#><#69101#> and <#69102#><#50922#>next@<#50922#><#50923#>cmu<#50923#><#69102#> in the hand-evaluated program. Then evaluate <#69103#><#50924#>((second<#50924#>\ <#50925#>lights))<#50925#><#69103#> in the context of these definitions.~ external Solution<#69104#><#69104#> <#50931#>Exercise 39.1.3<#50931#> Develop the function <#69105#><#50933#>draw-light<#50933#><#69105#>. It realizes the view part of the traffic light simulation in figure~#figmultipletl#50934>. Each traffic light should be as tall as the canvas, delineated by solid lines on the left and right. The suggested dimensions of a single light are
<#50939#>(define<#50939#> <#50940#>WIDTH<#50940#> <#50941#>50)<#50941#>
<#50942#>(define<#50942#> <#50943#>RADIUS<#50943#> <#50944#>20)<#50944#> 
<#50945#>(define<#50945#> <#50946#>DISTANCE-BETWEEN-BULBS<#50946#> <#50947#>10)<#50947#> 
<#50948#>;; the minimum canvas height<#50948#> 
<#50949#>(d<#50949#><#50950#>efine<#50950#> <#50951#>HEIGHT<#50951#> 
  <#50952#>(+<#50952#> <#50953#>DISTANCE-BETWEEN-BULBS<#50953#> 
     <#50954#>(*<#50954#> <#50955#>2<#50955#> <#50956#>RADIUS)<#50956#> 
     <#50957#>DISTANCE-BETWEEN-BULBS<#50957#> 
     <#50958#>(*<#50958#> <#50959#>2<#50959#> <#50960#>RADIUS)<#50960#> 
     <#50961#>DISTANCE-BETWEEN-BULBS<#50961#> 
     <#50962#>(*<#50962#> <#50963#>2<#50963#> <#50964#>RADIUS)<#50964#> 
     <#50965#>DISTANCE-BETWEEN-BULBS))<#50965#> 
Develop the necessary definitons separately from the rest of the traffic light program, then create a single function definition using <#69106#><#50969#>local<#50969#><#69106#>.~ external Solution<#69107#><#69107#>
Now suppose we wish to provide the additional service of resetting an individual traffic light. That is, in addition to switching from the current color to the next, an operator should be able to set a traffic light to red. The function for doing so already exists: <#69108#><#50977#>init-traffic-light<#50977#><#69108#>. It sets <#69109#><#50978#>current-color<#50978#><#69109#> to <#69110#><#50979#>'<#50979#><#50980#>red<#50980#><#69110#> and re-draws the image on the canvas. But, <#69111#><#50981#>init-traffic-light<#50981#><#69111#> is inaccessible because it is defined within the <#69112#><#50982#>local<#50982#>-expression<#69112#> of <#69113#><#50983#>make-traffic-light<#50983#><#69113#>. If we wish the function to be visible, it must be the result of <#69114#><#50984#>make-traffic-light<#50984#><#69114#> just like <#69115#><#50985#>next<#50985#><#69115#>. To make both <#69116#><#50986#>next<#50986#><#69116#> and <#69117#><#50987#>init-traffic-light<#50987#><#69117#> a result of <#69118#><#50988#>make-traffic-light<#50988#><#69118#> requires some way of combining the two functions into a single value. Since functions are values in Scheme, we could combine the two functions in a list, a structure, or even a vector. Another possibility is to combine the two functions in a third function. Here we discuss this third possibility because it is an important technique in the context of managing state variables and services. We call the new kind of function <#69119#><#50989#>service-manager<#50989#><#69119#>, because it hides and manages functions that implement services. The function accepts two symbols:
  1. <#69120#><#50991#>'<#50991#><#50992#>next<#50992#><#69120#>, which indicates that <#69121#><#50993#>(next)<#50993#><#69121#> should be evaluated
  2. <#69122#><#50994#>'<#50994#><#50995#>reset<#50995#><#69122#>, which indicates that <#69123#><#50996#>(reset)<#50996#><#69123#> should be evaluated.
Furthermore, the function is the result of the revised version of <#69124#><#50998#>make-traffic-light<#50998#><#69124#>. Figure~#figmultipletlreset#50999> contains the modified definition of <#69125#><#51000#>make-traffic-light<#51000#><#69125#>. Since an operator may mistakenly apply functions to inappropriate arguments, <#69126#><#51001#>service-manager<#51001#><#69126#> is a checked function in the sense of section~#secinputerrors#51002>. It signals an error if the input is a symbol other than <#69127#><#51003#>'<#51003#><#51004#>next<#51004#><#69127#> or <#69128#><#51005#>'<#51005#><#51006#>reset<#51006#><#69128#>.
<#71935#>;; <#69129#><#51011#>make-traffic-light<#51011#> <#51012#>:<#51012#> <#51013#>symbol<#51013#> <#51014#>number<#51014#> <#51015#><#51015#><#51016#>-;SPMgt;<#51016#><#51017#><#51017#> <#51018#>(symbol<#51018#> <#51019#><#51019#><#51020#>-;SPMgt;<#51020#><#51021#><#51021#> <#51022#>true)<#51022#><#69129#><#71935#>
<#71936#>;; to create a red light with <#69130#><#51023#>(make-posn<#51023#> <#51024#>x-posn<#51024#> <#51025#>0)<#51025#><#69130#> as the upper-left corner<#71936#> 
<#51026#>;; effect: draw the traffic light on the canvas<#51026#> 
<#51027#>(d<#51027#><#51028#>efine<#51028#> <#51029#>(make-traffic-light<#51029#> <#51030#>street<#51030#> <#51031#>x-posn)<#51031#> 
  <#51032#>(l<#51032#><#51033#>ocal<#51033#> <#51034#>(<#51034#><#69131#>;; <#51035#>Model:<#51035#> <#69131#> 
          <#71937#>;; <#69132#><#51036#>current-color<#51036#> <#51037#>:<#51037#> <#51038#>TL-color<#51038#><#69132#><#71937#> 
          <#51039#>;; to keep track of the current color of the traffic light<#51039#> 
          <#51040#>(define<#51040#> <#51041#>current-color<#51041#> <#51042#>'<#51042#><#51043#>red)<#51043#> 
          
          <#71938#>;; <#69133#><#51044#>init-traffic-light<#51044#> <#51045#>:<#51045#> <#51046#><#51046#><#51047#>-;SPMgt;<#51047#><#51048#><#51048#> <#51049#>true<#51049#><#69133#><#71938#> 
          <#71939#>;; to (re)set <#69134#><#51050#>current-color<#51050#><#69134#> to red and to (re)create the view <#71939#> 
          <#51051#>(define<#51051#> <#51052#>(init-traffic-light)<#51052#> <#51053#>...)<#51053#> 
          
          <#71940#>;; <#69135#><#51054#>next<#51054#> <#51055#>:<#51055#> <#51056#><#51056#><#51057#>-;SPMgt;<#51057#><#51058#><#51058#> <#51059#>true<#51059#><#69135#><#71940#> 
          <#71941#>;; effect: to change <#69136#><#51060#>current-color<#51060#><#69136#> from <#69137#><#51061#>'<#51061#><#51062#>green<#51062#><#69137#> to <#69138#><#51063#>'<#51063#><#51064#>yellow<#51064#><#69138#>, <#71941#> 
          <#71942#>;; <#69139#><#51065#>'<#51065#><#51066#>yellow<#51066#><#69139#> to <#69140#><#51067#>'<#51067#><#51068#>red<#51068#><#69140#>, and <#69141#><#51069#>'<#51069#><#51070#>red<#51070#><#69141#> to <#69142#><#51071#>'<#51071#><#51072#>green<#51072#><#69142#><#71942#> 
          <#51073#>(define<#51073#> <#51074#>(next)<#51074#> <#51075#>...)<#51075#> 
          
          <#71943#>;; <#69143#><#51076#>next-color<#51076#> <#51077#>:<#51077#> <#51078#>TL-color<#51078#> <#51079#><#51079#><#51080#>-;SPMgt;<#51080#><#51081#><#51081#> <#51082#>TL-color<#51082#><#69143#><#71943#> 
          <#71944#>;; to compute the successor of <#69144#><#51083#>current-color<#51083#><#69144#> based on the traffic laws<#71944#> 
          <#51084#>(define<#51084#> <#51085#>(next-color<#51085#> <#51086#>current-color)<#51086#> <#51087#>...)<#51087#> 
          <#71945#>;; <#69145#><#51088#>service-manager<#51088#> <#51089#>:<#51089#> <#51090#>(symbol<#51090#> <#51091#><#51091#><#51092#>-;SPMgt;<#51092#><#51093#><#51093#> <#51094#>true)<#51094#><#69145#><#71945#> 
          <#71946#>;; to apply either <#69146#><#51095#>next<#51095#><#69146#> or <#69147#><#51096#>init-traffic-light<#51096#><#69147#><#71946#> 
          <#51097#>(d<#51097#><#51098#>efine<#51098#> <#51099#>(service-manager<#51099#> <#51100#>msg)<#51100#> 
            <#51101#>(c<#51101#><#51102#>ond<#51102#> 
              <#51103#>[<#51103#><#51104#>(symbol=?<#51104#> <#51105#>msg<#51105#> <#51106#>'<#51106#><#51107#>next)<#51107#> <#51108#>(next)]<#51108#> 
              <#51109#>[<#51109#><#51110#>(symbol=?<#51110#> <#51111#>msg<#51111#> <#51112#>'<#51112#><#51113#>reset)<#51113#> <#51114#>(init-traffic-light)]<#51114#> 
              <#51115#>[<#51115#><#51116#>else<#51116#> <#51117#>(error<#51117#> <#51118#>'<#51118#><#51119#>traffic-light<#51119#> <#51120#>``message<#51120#> <#51121#>not<#51121#> <#51122#>understood'')]<#51122#><#51123#>)))<#51123#> 
    <#51124#>(b<#51124#><#51125#>egin<#51125#> 
      <#71947#>;; Initialize and produce <#69148#><#51126#>service-manager<#51126#><#69148#><#71947#> 
      <#51127#>(init-traffic-light)<#51127#> 
      <#51128#>service-manager)))<#51128#> 
<#51132#>Figure: Managing multiple traffic lights with a reset service<#51132#>
We use the new <#69149#><#51134#>make-traffic-light<#51134#><#69149#> function exactly like the old one:
<#51139#>;; create the canvas first <#51139#>
<#51140#>(start<#51140#> <#51141#>300<#51141#> <#51142#>160)<#51142#> 
<#71948#>;; <#69150#><#51143#>lights<#51143#> <#51144#>:<#51144#> <#51145#>(listof<#51145#> <#51146#>traffic-light)<#51146#><#69150#><#71948#> 
<#51147#>;; to manage the lights along Sunrise <#51147#> 
<#51148#>(d<#51148#><#51149#>efine<#51149#> <#51150#>lights<#51150#> 
  <#51151#>(list<#51151#> <#51152#>(make-traffic-light<#51152#> <#51153#>'<#51153#><#51154#>sunrise@<#51154#><#51155#>rice<#51155#> <#51156#>50)<#51156#> 
        <#51157#>(make-traffic-light<#51157#> <#51158#>'<#51158#><#51159#>sunrise@<#51159#><#51160#>cmu<#51160#> <#51161#>150)))<#51161#> 
The result is, however, that now every traffic light is represented as a function on symbols:
<#51169#>;SPMgt;<#51169#> <#51170#>((second<#51170#> <#51171#>lights)<#51171#> <#51172#>'<#51172#><#51173#>next)<#51173#> 
<#51174#>true<#51174#> 
<#51175#>;SPMgt;<#51175#> <#51176#>(andmap<#51176#> <#51177#>(lambda<#51177#> <#51178#>(a-light)<#51178#> <#51179#>(a-light<#51179#> <#51180#>'<#51180#><#51181#>reset))<#51181#> <#51182#>lights)<#51182#> 
<#51183#>true<#51183#> 
The first interaction switches the initially red light labeld <#69151#><#51187#>'<#51187#><#51188#>sunrise@<#51188#><#51189#>cmu<#51189#><#69151#> to <#69152#><#51190#>'<#51190#><#51191#>green<#51191#><#69152#>. The second one changes the state of every light back to <#69153#><#51192#>'<#51192#><#51193#>red<#51193#><#69153#> skipping the <#69154#><#51194#>'<#51194#><#51195#>yellow<#51195#><#69154#> stage for the light at <#69155#><#51196#>'<#51196#><#51197#>sunrise@<#51197#><#51198#>cmu<#51198#><#69155#>.
<#51201#>Exercise 39.1.4<#51201#> Complete the definition of the program in figure~#figmultipletlreset#51203>, using the function from exercise~#exmultipltldraw#51204>. Then use DrScheme's <#51205#>Interactions<#51205#> window to switch and reset traffic lights.~ external Solution<#69156#><#69156#> <#51211#>Exercise 39.1.5<#51211#> Evaluate the above program by hand and confirm that the light labeled <#69157#><#51213#>'<#51213#><#51214#>sunrise@<#51214#><#51215#>rice<#51215#><#69157#> switches from <#69158#><#51216#>'<#51216#><#51217#>green<#51217#><#69158#> directly back to <#69159#><#51218#>'<#51218#><#51219#>red<#51219#><#69159#>.~ external Solution<#69160#><#69160#>
For the address-book example from part~#partstate#51227>, the need for managing two services is even more apparent. After all, the motivating idea behind the example is that users can access one state variable with two different services: <#69161#><#51228#>add-to-address-book<#51228#><#69161#> for adding new entries and <#69162#><#51229#>lookup<#51229#><#69162#> for looking up the phone number for a given name. Following our encapsulation receipe, we must
  1. define a function <#69163#><#51231#>make-address-book<#51231#><#69163#> whose body is a <#69164#><#51232#>local<#51232#>-expression<#69164#>;
  2. place the definitions in this <#69165#><#51233#>local<#51233#>-expression<#69165#>; and
  3. introduce a function called <#69166#><#51234#>service-manager<#51234#><#69166#> to manage the two services.
By now, we have the first two steps firmly under control; the last one, however, is complex here, because unlike in the previous case, the two functions that implement the services consume different numbers of arguments and produce different kinds of results. Let's first agree on the inputs for <#69167#><#51236#>service-manager<#51236#><#69167#>. Two good mnemonic symbols are <#69168#><#51237#>'<#51237#><#51238#>add<#51238#><#69168#> for adding phone numbers and <#69169#><#51239#>'<#51239#><#51240#>search<#51240#><#69169#> for looking up the number for some given name. This suggests the following template:
<#51245#>(d<#51245#><#51246#>efine<#51246#> <#51247#>(service-manager<#51247#> <#51248#>msg)<#51248#>
  <#51249#>(c<#51249#><#51250#>ond<#51250#> 
    <#51251#>[<#51251#><#51252#>(symbol=?<#51252#> <#51253#>msg<#51253#> <#51254#>'<#51254#><#51255#>add)<#51255#> <#51256#>...<#51256#> <#51257#>A<#51257#> <#51258#>...]<#51258#> 
    <#51259#>[<#51259#><#51260#>(symbol=?<#51260#> <#51261#>msg<#51261#> <#51262#>'<#51262#><#51263#>search)<#51263#> <#51264#>...<#51264#> <#51265#>B<#51265#> <#51266#>...]<#51266#> 
    <#51267#>[<#51267#><#51268#>else<#51268#> <#51269#>(error<#51269#> <#51270#>'<#51270#><#51271#>address-book<#51271#> <#51272#>``message<#51272#> <#51273#>not<#51273#> <#51274#>understood'')]<#51274#><#51275#>))<#51275#> 
The problem is that it is not clear how to replace <#69170#><#51279#>A<#51279#><#69170#> and <#69171#><#51280#>B<#51280#><#69171#> with valid Scheme expressions that compute the appropriate value and effect. For <#69172#><#51281#>A<#51281#><#69172#>, we not only need <#69173#><#51282#>msg<#51282#><#69173#> but also a name and a phone number. For <#69174#><#51283#>B<#51283#><#69174#>, we need the name. One solution is to produce functions that consume the additional arguments and then perform the appropriate computation. In other words, <#69175#><#51284#>service-manager<#51284#><#69175#> is a now function that produces a function for two symbols. Since we have not encountered this kind of result before, we introduce a new form of data definition:
An <#69176#><#51286#>address-book<#51286#><#69176#> is an interface:
  1. <#69177#><#51288#>'<#51288#><#51289#>add<#51289#><#69177#> :: <#69178#><#51290#>symbol<#51290#>\ <#51291#>number<#51291#>\ <#51292#><#51292#><#51293#>-;SPMgt;<#51293#><#51294#><#51294#>\ <#51295#>void<#51295#><#69178#>
  2. <#69179#><#51296#>'<#51296#><#51297#>search<#51297#><#69179#> :: <#69180#><#51298#>symbol<#51298#>\ <#51299#><#51299#><#51300#>-;SPMgt;<#51300#><#51301#><#51301#>\ <#51302#>number<#51302#><#69180#>
The data definition refers to the concept of <#69181#><#51305#>INTERFACE<#51305#><#69181#>, which is a function that consumes a finite number of symbols and produces functions with different types in return. Because this kind of function is radically different from what we have seen before, we use a different name. Now it is possible to write a contract and a purpose statement:
<#71949#>;; <#69182#><#51310#>service-manager<#51310#> <#51311#>:<#51311#> <#51312#>address-book<#51312#><#69182#><#71949#>
<#51313#>;; to manage addition to, and searches in, the address book <#51313#> 
<#51314#>(define<#51314#> <#51315#>(service-manager<#51315#> <#51316#>msg)<#51316#> <#51317#>...)<#51317#> 
To define the function, we distinguish the two cases. In the case of <#69183#><#51321#>'<#51321#><#51322#>add<#51322#><#69183#>, it is obvious what we produce: <#69184#><#51323#>add-to-address-book<#51323#><#69184#>. In the case of <#69185#><#51324#>'<#51324#><#51325#>search<#51325#><#69185#>, we need a function that consumes a symbol and then applies <#69186#><#51326#>lookup<#51326#><#69186#> to this symbol and the <#69187#><#51327#>local<#51327#><#69187#>ly defined <#69188#><#51328#>address-book<#51328#><#69188#>:
<#51333#>(l<#51333#><#51334#>ambda<#51334#> <#51335#>(name)<#51335#>
  <#51336#>(lookup<#51336#> <#51337#>name<#51337#> <#51338#>address-book))<#51338#> 
The use of <#69189#><#51342#>lambda<#51342#><#69189#> is appropriate here, because the function doesn't need a name.
<#71950#>;; <#69190#><#51347#>make-address-book<#51347#> <#51348#>:<#51348#> <#51349#>string<#51349#> <#51350#><#51350#><#51351#>-;SPMgt;<#51351#><#51352#><#51352#> <#51353#>address-book<#51353#><#69190#><#71950#>
<#51354#>;; to create a function that manages all the services for a hidden address book<#51354#> 
<#51355#>(d<#51355#><#51356#>efine<#51356#> <#51357#>(make-address-book<#51357#> <#51358#>title)<#51358#> 
  <#51359#>(l<#51359#><#51360#>ocal<#51360#> <#51361#>(<#51361#><#51362#>(define-struct<#51362#> <#51363#>entry<#51363#> <#51364#>(name<#51364#> <#51365#>number))<#51365#> 
          <#71951#>;; <#69191#><#51366#>address-book<#51366#> <#51367#>:<#51367#> <#51368#>(listof<#51368#> <#51369#>(list<#51369#> <#51370#>name<#51370#> <#51371#>number))<#51371#><#69191#><#71951#> 
          <#51372#>;; to maintain a list of name-phone number associations<#51372#> 
          <#51373#>(define<#51373#> <#51374#>address-book<#51374#> <#51375#>empty)<#51375#> 
          
          <#71952#>;; <#69192#><#51376#>add-to-address-book<#51376#> <#51377#>:<#51377#> <#51378#>symbol<#51378#> <#51379#>number<#51379#> <#51380#><#51380#><#51381#>-;SPMgt;<#51381#><#51382#><#51382#> <#51383#>void<#51383#><#69192#><#71952#> 
          <#71953#>;; effect: to add a name-phone number association to <#69193#><#51384#>address-book<#51384#><#69193#><#71953#> 
          <#51385#>(d<#51385#><#51386#>efine<#51386#> <#51387#>(add-to-address-book<#51387#> <#51388#>name<#51388#> <#51389#>phone)<#51389#> 
            <#51390#>(set!<#51390#> <#51391#>address-book<#51391#> <#51392#>(cons<#51392#> <#51393#>(make-entry<#51393#> <#51394#>name<#51394#> <#51395#>phone)<#51395#> <#51396#>address-book)))<#51396#> 
          
          <#71954#>;; <#69194#><#51397#>lookup<#51397#> <#51398#>:<#51398#> <#51399#>symbol<#51399#> <#51400#>(listof<#51400#> <#51401#>(list<#51401#> <#51402#>symbol<#51402#> <#51403#>number))<#51403#> <#51404#><#51404#><#51405#>-;SPMgt;<#51405#><#51406#><#51406#> <#51407#>number<#51407#> <#51408#>or<#51408#> <#51409#>false<#51409#><#69194#><#71954#> 
          <#71955#>;; to lookup the phone number for <#69195#><#51410#>name<#51410#><#69195#> in <#69196#><#51411#>address-book<#51411#><#69196#><#71955#> 
          <#51412#>(d<#51412#><#51413#>efine<#51413#> <#51414#>(lookup<#51414#> <#51415#>name<#51415#> <#51416#>ab)<#51416#> 
            <#51417#>(c<#51417#><#51418#>ond<#51418#> 
              <#51419#>[<#51419#><#51420#>(empty?<#51420#> <#51421#>ab)<#51421#> <#51422#>false<#51422#><#51423#>]<#51423#> 
              <#51424#>[<#51424#><#51425#>else<#51425#> <#51426#>(c<#51426#><#51427#>ond<#51427#> 
                      <#51428#>[<#51428#><#51429#>(symbol=?<#51429#> <#51430#>(entry-name<#51430#> <#51431#>(first<#51431#> <#51432#>ab))<#51432#> <#51433#>name)<#51433#> 
                       <#51434#>(entry-number<#51434#> <#51435#>(first<#51435#> <#51436#>ab))]<#51436#> 
                      <#51437#>[<#51437#><#51438#>else<#51438#> <#51439#>(lookup<#51439#> <#51440#>name<#51440#> <#51441#>(rest<#51441#> <#51442#>ab))]<#51442#><#51443#>)]<#51443#><#51444#>))<#51444#> 
          
          <#71956#>;; <#69197#><#51445#>service-manager<#51445#> <#51446#>:<#51446#> <#51447#>address-book<#51447#> <#51448#>object<#51448#><#69197#><#71956#> 
          <#51449#>;; to manage addition to, and searches in, the address book <#51449#> 
          <#51450#>(d<#51450#><#51451#>efine<#51451#> <#51452#>(service-manager<#51452#> <#51453#>msg)<#51453#> 
            <#51454#>(c<#51454#><#51455#>ond<#51455#> 
              <#51456#>[<#51456#><#51457#>(symbol=?<#51457#> <#51458#>msg<#51458#> <#51459#>'<#51459#><#51460#>add)<#51460#> 
               <#51461#>add-to-address-book]<#51461#> 
              <#51462#>[<#51462#><#51463#>(symbol=?<#51463#> <#51464#>msg<#51464#> <#51465#>'<#51465#><#51466#>search)<#51466#> 
               <#51467#>(l<#51467#><#51468#>ambda<#51468#> <#51469#>(name)<#51469#> 
                 <#51470#>(lookup<#51470#> <#51471#>name<#51471#> <#51472#>address-book))]<#51472#> 
              <#51473#>[<#51473#><#51474#>else<#51474#> <#51475#>(error<#51475#> <#51476#>'<#51476#><#51477#>address-book<#51477#> <#51478#>``message<#51478#> <#51479#>not<#51479#> <#51480#>understood'')]<#51480#><#51481#>)))<#51481#> 
    <#51482#>service-manager))<#51482#> 
<#51486#>Figure: Managing multiple address books<#51486#>
Figure~#figmultipleab#51488> shows the complete definition of <#69198#><#51489#>make-address-book<#51489#><#69198#>. The definition is standard by now. It consists of a <#69199#><#51490#>local<#51490#>-expression<#69199#>, which in turn produces the <#69200#><#51491#>local<#51491#><#69200#>ly defined <#69201#><#51492#>service-manager<#51492#><#69201#> as the result. There is no need for an initializer because the only state variable is immediately initialized and there is no graphical view. To use the address books, we first create one with <#69202#><#51493#>make-address-book<#51493#><#69202#>, using an appropriate title:
<#71957#>;; <#69203#><#51498#>friends<#51498#> <#51499#>:<#51499#> <#51500#>an<#51500#> <#51501#>address<#51501#> <#51502#>book<#51502#><#69203#><#71957#>
<#51503#>;; to maintain an address book for friends <#51503#> 
<#51504#>(d<#51504#><#51505#>efine<#51505#> <#51506#>friends<#51506#> 
  <#51507#>(make-address-book<#51507#> <#51508#>``Friends<#51508#> <#51509#>of<#51509#> <#51510#>Charles''))<#51510#> 
<#71958#>;; <#69204#><#51511#>business<#51511#> <#51512#>:<#51512#> <#51513#>an<#51513#> <#51514#>address<#51514#> <#51515#>book<#51515#><#69204#><#71958#> 
<#51516#>;; to maintain an address book for business colleagues<#51516#> 
<#51517#>(d<#51517#><#51518#>efine<#51518#> <#51519#>business<#51519#> 
  <#51520#>(make-address-book<#51520#> <#51521#>``Colleagues<#51521#> <#51522#>@<#51522#> <#51523#>Rice,<#51523#> <#51524#>Inc.''))<#51524#> 
Then we can add names and phone numbers to the address book, or we can retrieve numbers as desired:
<#51532#>;SPMgt;<#51532#> <#51533#>((friends<#51533#> <#51534#>'<#51534#><#51535#>add)<#51535#> <#51536#>'<#51536#><#51537#>Bill<#51537#> <#51538#>2)<#51538#>
<#51539#>;SPMgt;<#51539#> <#51540#>((friends<#51540#> <#51541#>'<#51541#><#51542#>add)<#51542#> <#51543#>'<#51543#><#51544#>Sally<#51544#> <#51545#>3)<#51545#> 
<#51546#>;SPMgt;<#51546#> <#51547#>((friends<#51547#> <#51548#>'<#51548#><#51549#>add)<#51549#> <#51550#>'<#51550#><#51551#>Dave<#51551#> <#51552#>4)<#51552#> 
<#51553#>;SPMgt;<#51553#> <#51554#>((business<#51554#> <#51555#>'<#51555#><#51556#>add)<#51556#> <#51557#>'<#51557#><#51558#>Emil<#51558#> <#51559#>5)<#51559#> 
<#51560#>;SPMgt;<#51560#> <#51561#>((business<#51561#> <#51562#>'<#51562#><#51563#>add)<#51563#> <#51564#>'<#51564#><#51565#>Faye<#51565#> <#51566#>18)<#51566#> 
In this case, we added three entries to the address book named <#69205#><#51570#>friends<#51570#><#69205#> and two to the one called <#69206#><#51571#>business<#51571#><#69206#>. An addition to, say, <#69207#><#51572#>friends<#51572#><#69207#> works in two steps. The first step is to apply <#69208#><#51573#>friends<#51573#><#69208#> to <#69209#><#51574#>'<#51574#><#51575#>add<#51575#><#69209#>. This yields the (hidden) function <#69210#><#51576#>add-to-address-book<#51576#><#69210#>. The second step is to apply this resulting function to a name and a number. In a similar vein, looking up a phone number also works in two steps. The application of, say, <#69211#><#51577#>friends<#51577#><#69211#> to <#69212#><#51578#>'<#51578#><#51579#>search<#51579#><#69212#> yields a function that consumes a name. This function is then applied to a symbol:
<#51584#>;SPMgt;<#51584#> <#51585#>((friends<#51585#> <#51586#>'<#51586#><#51587#>search)<#51587#> <#51588#>'<#51588#><#51589#>Bill)<#51589#>
<#51590#>2<#51590#> 
<#51591#>;SPMgt;<#51591#> <#51592#>((business<#51592#> <#51593#>'<#51593#><#51594#>search)<#51594#> <#51595#>'<#51595#><#51596#>Bill)<#51596#> 
<#51597#>false<#51597#> 
The two applications show that the number for <#69213#><#51601#>'<#51601#><#51602#>Bill<#51602#><#69213#> in <#69214#><#51603#>friends<#51603#><#69214#> is <#69215#><#51604#>2<#51604#><#69215#> and that there is no number for <#69216#><#51605#>'<#51605#><#51606#>Bill<#51606#><#69216#> in colleagues. According to the above additions, that's exactly what we should expect. Of course, we could also co-mingle the two actions in the <#51607#>Interactions<#51607#> window, adding and searching for phone numbers at will.
<#51610#>Exercise 39.1.6<#51610#> Develop an interface definition for the results of the revised version of <#69217#><#51612#>make-traffic-light<#51612#><#69217#> (see figure~#figmultipletlreset#51613>).~ external Solution<#69218#><#69218#> <#51619#>Exercise 39.1.7<#51619#> Show the top-level definitions that the evaluation of <#69219#><#51621#>friends<#51621#><#69219#> and <#69220#><#51622#>colleagues<#51622#><#69220#> creates. What is the state of these definitions after the five <#69221#><#51623#>'<#51623#><#51624#>add<#51624#><#69221#> expressions have been evaluated? Evaluate <#69222#><#51625#>((friends<#51625#>\ <#51626#>'<#51626#><#51627#>search)<#51627#>\ <#51628#>'<#51628#><#51629#>Bill)<#51629#><#69222#> in this context.~ external Solution<#69223#><#69223#> <#51635#>Exercise 39.1.8<#51635#> Design <#69224#><#51637#>gui-for-address-boook<#51637#><#69224#>. The function consumes a list of strings and creates a new address book for each one of them. It also creates and displays a graphical user interface for an address book with a choice menu that lets users choose to which address book they want to add an entry and in which address book the program should search for a number.~ external Solution<#69225#><#69225#>