The extension of our language with functions as values not only introduces
new powers for the programmer but also new possibilities for errors. Recall
that there are three kinds of errors: syntax errors, run-time (or semantics)
errors, and logical errors. <#50131#>Advanced Student<#50131#> Scheme turns a class of
syntactic errors of <#50132#>Beginning Student<#50132#> Scheme into run-time errors. It also
introduces a new form of logical error.
Consider the following program:
<#71917#>;; <#69029#><#50137#>how-many-in-list<#50137#> <#50138#>:<#50138#> <#50139#>(listof<#50139#> <#50140#>X)<#50140#> <#50141#><#50141#><#50142#>-;SPMgt;<#50142#><#50143#><#50143#> <#50144#>N<#50144#><#69029#><#71917#>
<#71918#>;; to count how many items <#69030#><#50145#>alist<#50145#><#69030#> contains <#71918#>
<#50146#>(d<#50146#><#50147#>efine<#50147#> <#50148#>(how-many-in-list<#50148#> <#50149#>alist)<#50149#>
<#50150#>(c<#50150#><#50151#>ond<#50151#>
<#50152#>[<#50152#><#50153#>empty?<#50153#> <#50154#>(alist)]<#50154#>
<#50155#>[<#50155#><#50156#>else<#50156#> <#50157#>(+<#50157#> <#50158#>(how-many-in-list<#50158#> <#50159#>(rest<#50159#> <#50160#>alist))<#50160#> <#50161#>1)]<#50161#><#50162#>))<#50162#>
In <#50166#>Beginning Student<#50166#> or <#50167#>Intermediate Student<#50167#> Scheme,
DrScheme would have signaled a syntax error because <#69031#><#50168#>alist<#50168#><#69031#> is the
parameter to a function but is also used as a function. Because functions
are values in <#50169#>Advanced Student<#50169#> Scheme, DrScheme must now accept this
function definition as syntactially correct. When the function is applied
to <#69032#><#50170#>empty<#50170#><#69032#> or any other list value, however, DrScheme soon applies
<#69033#><#50171#>empty<#50171#><#69033#> to no arguments, which is a run-time error. After all, lists
are not functions. DrScheme signals any attempt to apply a non-function
immediately with an error message and stops the evaluation.
The second form of error is logical. That is, a program that suffers from
this form of error doesn't produce a syntax or a run-time error
message. Instead, it produces wrong answers. Take a look at the following
two definitions:
<#50176#>(d<#50176#><#50177#>efine<#50177#> <#50178#>flip1<#50178#>
<#50179#>(l<#50179#><#50180#>ocal<#50180#> <#50181#>((define<#50181#> <#50182#>state<#50182#> <#50183#>1))<#50183#>
<#50184#>(l<#50184#><#50185#>ambda<#50185#> <#50186#>()<#50186#>
<#50187#>(b<#50187#><#50188#>egin<#50188#>
<#50189#>(set!<#50189#> <#50190#>state<#50190#> <#50191#>(-<#50191#> <#50192#>1<#50192#> <#50193#>state))<#50193#>
<#50194#>state))))<#50194#>
<#50200#>(d<#50200#><#50201#>efine<#50201#> <#50202#>flip2<#50202#>
<#50203#>(l<#50203#><#50204#>ambda<#50204#> <#50205#>()<#50205#>
<#50206#>(l<#50206#><#50207#>ocal<#50207#> <#50208#>((define<#50208#> <#50209#>state<#50209#> <#50210#>1))<#50210#>
<#50211#>(b<#50211#><#50212#>egin<#50212#>
<#50213#>(set!<#50213#> <#50214#>state<#50214#> <#50215#>(-<#50215#> <#50216#>1<#50216#> <#50217#>state))<#50217#>
<#50218#>state))))<#50218#>
They differ in the order of two lines. One introduces a <#69034#><#50222#>local<#50222#><#69034#>
definition whose body evaluates to a function. The other defines a function
whose body contains a <#50223#>local<#50223#>-expression. According to our rules, the definition on
the left rewrites to
<#50228#>(define<#50228#> <#50229#>state1<#50229#> <#50230#>1)<#50230#>
<#50231#>(d<#50231#><#50232#>efine<#50232#> <#50233#>flip1<#50233#>
<#50234#>(l<#50234#><#50235#>ambda<#50235#> <#50236#>()<#50236#>
<#50237#>(b<#50237#><#50238#>egin<#50238#>
<#50239#>(set!<#50239#> <#50240#>state1<#50240#> <#50241#>(-<#50241#> <#50242#>1<#50242#> <#50243#>state1))<#50243#>
<#50244#>state1)))<#50244#>
<#50250#>(d<#50250#><#50251#>efine<#50251#> <#50252#>flip2<#50252#>
<#50253#>(l<#50253#><#50254#>ambda<#50254#> <#50255#>()<#50255#>
<#50256#>(l<#50256#><#50257#>ocal<#50257#> <#50258#>((define<#50258#> <#50259#>state<#50259#> <#50260#>1))<#50260#>
<#50261#>(b<#50261#><#50262#>egin<#50262#>
<#50263#>(set!<#50263#> <#50264#>state<#50264#> <#50265#>(-<#50265#> <#50266#>1<#50266#> <#50267#>state))<#50267#>
<#50268#>state))))<#50268#>
The one on the right already associates a name with a function.
Let us now see how the two functions have radically different behaviors. To
do so, we evaluate the expressions
<#50276#>(and<#50276#> <#50277#>(=<#50277#> <#50278#>(flip1)<#50278#> <#50279#>0)<#50279#>
<#50280#>(=<#50280#> <#50281#>(flip1)<#50281#> <#50282#>1)<#50282#>
<#50283#>(=<#50283#> <#50284#>(flip1)<#50284#> <#50285#>0))<#50285#>
<#50291#>(and<#50291#> <#50292#>(=<#50292#> <#50293#>(flip2)<#50293#> <#50294#>0)<#50294#>
<#50295#>(=<#50295#> <#50296#>(flip2)<#50296#> <#50297#>1)<#50297#>
<#50298#>(=<#50298#> <#50299#>(flip2)<#50299#> <#50300#>0))<#50300#>
in the context of the respective definitions.
Here are the first four steps of the evaluation for the left-hand
expression:
<#50308#>(define<#50308#> <#50309#>state1<#50309#> <#50310#>1)<#50310#>
<#50311#>(and<#50311#> <#50312#>(=<#50312#> <#50313#>(flip1)<#50313#> <#50314#>0)<#50314#>
<#50315#>(=<#50315#> <#50316#>(flip1)<#50316#> <#50317#>1)<#50317#>
<#50318#>(=<#50318#> <#50319#>(flip1)<#50319#> <#50320#>0))<#50320#>
<#50321#>=<#50321#> <#50322#>(define<#50322#> <#50323#>state1<#50323#> <#50324#>1)<#50324#>
<#50325#>(and<#50325#> <#50326#>(=<#50326#> <#50327#>(b<#50327#><#50328#>egin<#50328#>
<#50329#>(set!<#50329#> <#50330#>state1<#50330#> <#50331#>(-<#50331#> <#50332#>1<#50332#> <#50333#>state1))<#50333#>
<#50334#>state1)<#50334#>
<#50335#>0)<#50335#>
<#50336#>(=<#50336#> <#50337#>(flip1)<#50337#> <#50338#>1)<#50338#>
<#50339#>(=<#50339#> <#50340#>(flip1)<#50340#> <#50341#>0))<#50341#>
<#50342#>=<#50342#> <#50343#>(define<#50343#> <#50344#>state1<#50344#> <#50345#>1)<#50345#>
<#50346#>(and<#50346#> <#50347#>(=<#50347#> <#50348#>(b<#50348#><#50349#>egin<#50349#>
<#50350#>(set!<#50350#> <#50351#>state1<#50351#> <#50352#>0)<#50352#>
<#50353#>state1)<#50353#>
<#50354#>0)<#50354#>
<#50355#>(=<#50355#> <#50356#>(flip1)<#50356#> <#50357#>1)<#50357#>
<#50358#>(=<#50358#> <#50359#>(flip1)<#50359#> <#50360#>0))<#50360#>
<#50361#>=<#50361#> <#50362#>(define<#50362#> <#50363#>state1<#50363#> <#50364#>0)<#50364#>
<#50365#>(and<#50365#> <#50366#>(=<#50366#> <#50367#>(b<#50367#><#50368#>egin<#50368#>
<#50369#>(<#50369#><#50370#>void<#50370#><#50371#>)<#50371#>
<#50372#>state1)<#50372#>
<#50373#>0)<#50373#>
<#50374#>(=<#50374#> <#50375#>(flip1)<#50375#> <#50376#>1)<#50376#>
<#50377#>(=<#50377#> <#50378#>(flip1)<#50378#> <#50379#>0))<#50379#>
<#50380#>=<#50380#> <#50381#>(define<#50381#> <#50382#>state1<#50382#> <#50383#>0)<#50383#>
<#50384#>(and<#50384#> <#50385#>(=<#50385#> <#50386#>0<#50386#> <#50387#>0)<#50387#>
<#50388#>(=<#50388#> <#50389#>(flip1)<#50389#> <#50390#>1)<#50390#>
<#50391#>(=<#50391#> <#50392#>(flip1)<#50392#> <#50393#>0))<#50393#>
The relevant definition context is the definition of <#69035#><#50397#>state1<#50397#><#69035#>, which
we see changing from <#69036#><#50398#>1<#50398#><#69036#> to <#69037#><#50399#>0<#50399#><#69037#> during the third step. From
this point, it is not difficult to validate that the expression produces
<#69038#><#50400#>true<#50400#><#69038#> and that <#69039#><#50401#>state1<#50401#><#69039#> ends up being <#69040#><#50402#>0<#50402#><#69040#>.
Compare this with the first three steps in the evaluation of the right-hand
expression:
<#50407#>(and<#50407#> <#50408#>(=<#50408#> <#50409#>(flip2)<#50409#> <#50410#>0)<#50410#>
<#50411#>(=<#50411#> <#50412#>(flip2)<#50412#> <#50413#>1)<#50413#>
<#50414#>(=<#50414#> <#50415#>(flip2)<#50415#> <#50416#>0))<#50416#>
<#50417#>=<#50417#> <#50418#>(and<#50418#> <#50419#>(=<#50419#> <#50420#>(l<#50420#><#50421#>ocal<#50421#> <#50422#>((define<#50422#> <#50423#>state<#50423#> <#50424#>1))<#50424#>
<#50425#>(b<#50425#><#50426#>egin<#50426#>
<#50427#>(set!<#50427#> <#50428#>state<#50428#> <#50429#>(-<#50429#> <#50430#>1<#50430#> <#50431#>state))<#50431#>
<#50432#>state))<#50432#>
<#50433#>0)<#50433#>
<#50434#>(=<#50434#> <#50435#>(flip2)<#50435#> <#50436#>1)<#50436#>
<#50437#>(=<#50437#> <#50438#>(flip2)<#50438#> <#50439#>0))<#50439#>
<#50440#>=<#50440#> <#50441#>(define<#50441#> <#50442#>state1<#50442#> <#50443#>1)<#50443#>
<#50444#>(and<#50444#> <#50445#>(=<#50445#> <#50446#>(b<#50446#><#50447#>egin<#50447#>
<#50448#>(set!<#50448#> <#50449#>state1<#50449#> <#50450#>(-<#50450#> <#50451#>1<#50451#> <#50452#>state1))<#50452#>
<#50453#>state1)<#50453#>
<#50454#>0)<#50454#>
<#50455#>(=<#50455#> <#50456#>(flip2)<#50456#> <#50457#>1)<#50457#>
<#50458#>(=<#50458#> <#50459#>(flip2)<#50459#> <#50460#>0))<#50460#>
<#50461#>=<#50461#> <#50462#>(define<#50462#> <#50463#>state1<#50463#> <#50464#>0)<#50464#>
<#50465#>(and<#50465#> <#50466#>(=<#50466#> <#50467#>0<#50467#> <#50468#>0)<#50468#>
<#50469#>(=<#50469#> <#50470#>(flip2)<#50470#> <#50471#>1)<#50471#>
<#50472#>(=<#50472#> <#50473#>(flip2)<#50473#> <#50474#>0))<#50474#>
The only definition that matters here is the one for <#69041#><#50478#>flip2<#50478#><#69041#>.
Superficially, the two evaluations are alike. But a closer look shows
that the second one differs from the first in crucial way. It creates the
definition for <#69042#><#50479#>state1<#50479#><#69042#> while the first evaluation started with such
a definition.
Here is the continuation of the second evaluation:
<#50484#>...<#50484#>
<#50485#>=<#50485#> <#50486#>(define<#50486#> <#50487#>state1<#50487#> <#50488#>0)<#50488#>
<#50489#>(and<#50489#> <#50490#>true<#50490#>
<#50491#>(=<#50491#> <#50492#>(l<#50492#><#50493#>ocal<#50493#> <#50494#>((define<#50494#> <#50495#>state<#50495#> <#50496#>1))<#50496#>
<#50497#>(b<#50497#><#50498#>egin<#50498#>
<#50499#>(set!<#50499#> <#50500#>state<#50500#> <#50501#>(-<#50501#> <#50502#>1<#50502#> <#50503#>state))<#50503#>
<#50504#>state))<#50504#>
<#50505#>1)<#50505#>
<#50506#>(=<#50506#> <#50507#>(flip2)<#50507#> <#50508#>0))<#50508#>
<#50509#>=<#50509#> <#50510#>(define<#50510#> <#50511#>state1<#50511#> <#50512#>0)<#50512#>
<#50513#>(define<#50513#> <#50514#>state2<#50514#> <#50515#>1)<#50515#>
<#50516#>(and<#50516#> <#50517#>true<#50517#>
<#50518#>(=<#50518#> <#50519#>(b<#50519#><#50520#>egin<#50520#>
<#50521#>(set!<#50521#> <#50522#>state2<#50522#> <#50523#>(-<#50523#> <#50524#>1<#50524#> <#50525#>state2))<#50525#>
<#50526#>state2)<#50526#>
<#50527#>1)<#50527#>
<#50528#>(=<#50528#> <#50529#>(flip2)<#50529#> <#50530#>0))<#50530#>
<#50531#>=<#50531#> <#50532#>(define<#50532#> <#50533#>state1<#50533#> <#50534#>0)<#50534#>
<#50535#>(define<#50535#> <#50536#>state2<#50536#> <#50537#>0)<#50537#>
<#50538#>(and<#50538#> <#50539#>true<#50539#>
<#50540#>(=<#50540#> <#50541#>(b<#50541#><#50542#>egin<#50542#>
<#50543#>(<#50543#><#50544#>void<#50544#><#50545#>)<#50545#>
<#50546#>state2)<#50546#>
<#50547#>1)<#50547#>
<#50548#>(=<#50548#> <#50549#>(flip2)<#50549#> <#50550#>0))<#50550#>
<#50551#>=<#50551#> <#50552#>(define<#50552#> <#50553#>state1<#50553#> <#50554#>0)<#50554#>
<#50555#>(define<#50555#> <#50556#>state2<#50556#> <#50557#>0)<#50557#>
<#50558#>(and<#50558#> <#50559#>true<#50559#>
<#50560#>(=<#50560#> <#50561#>0<#50561#> <#50562#>1)<#50562#>
<#50563#>(=<#50563#> <#50564#>(flip2)<#50564#> <#50565#>0))<#50565#>
It shows that <#69043#><#50569#>flip2<#50569#><#69043#> creates a new definition every time it is
applied and that it always produces <#69044#><#50570#>0<#50570#><#69044#>. Contrary to its name, it
does not flip the value of <#69045#><#50571#>state<#50571#><#69045#> upon every application. As a
result, the evaluation ends now with two new top-level definitions and the
value <#69046#><#50572#>false<#50572#><#69046#>.
The general moral is that a function defined in a <#50573#>local<#50573#>-expression\ is different
from a function whose body contains a <#50574#>local<#50574#>-expression. The first ensures that some
definitions are only accessible to a function. The definitions exists once
and only once for this function. In contrast, the second creates new
top-level the evaluation of the function body. In the next part of the
book, we exploit both ideas to create new kinds of programs.