[Prev][Next][Index][Thread]

Re: Closures



In article <3B720DA9.2060901@staffware-spokane.com>, Doug Hockin 
<dhockin@staffware-spokane.com> wrote:

> I'm new to Dylan and many of the concepts.  In the
> Dylan Reference Manual is an example that I can't
> follow.  It seems to me that there are errors in
> the text.  Am I missing something?
> 
> > The following example defines a function that returns score-card 
> > methods.
> > The method that is returned is closed over the score parameter. Each 
> > time
> > this method is called, it updates the score parameter and returns its 
> > new value.
> > 
> > define method make-score (points :: <number>)
> >   method (increase :: <number>)
> >     points := points + increase;
> >   end method;
> > end method make-score;
> > define constant score-david = make-score(100)
> > define constant score-diane = make-score(400)
> > score-david(0)
> > fi 100
> > score-david(10)
> > fi 110
> > score-david(10)
> > fi 120
> > score-diane(10)
> > fi 410
> > score-david(0)
> > fi 120
> > 
> > Each invocation of make-score creates a new binding for score, so each 
> > closure returned by make-score refers to a different binding. In this 
> > way, 
> > assignments to the variable made by one closure do not affect the value 
> > of 
> > the variable visible to other closures.
> 
> I don't see a "score parameter" anywhere.  Does it mean "points"?

Yes, it does.


> Could someone give a blow by blow of how the above works?

What languages have you used previously?  You can do exactly the same 
thing in many languages, including Lisp, Scheme, Perl, and I think, 
recently, Python.

In Perl:

[localhost:~/programs/perl] bruce% cat >score.pl
#!/usr/bin/perl

sub makeScore {
    my $points = shift;
    sub {
        my $increase = shift;
        $points += $increase;
    }
}

my $david = makeScore(100);
my $diane = makeScore(400);
print &$david(0), "\n";
print &$david(10), "\n";
print &$david(10), "\n";
print &$diane(10), "\n";
print &$david(0), "\n";
[localhost:~/programs/perl] bruce% perl score.pl 
100
110
120
410
120
[localhost:~/programs/perl] bruce% 


Semantically, the idea is that any function has access to the local 
variables and arguments of any functions that it is nested within.  This 
applies also to such old languages as Pascal and Algol, but not to C 
since C doesn't have nested functions (well, gcc supports them, but 
standard C doesn't).

The only difference here is that in Pascal such a reference is 
considered to be invalid after a function containing such a local 
variable returns.  So you can, say, pass a comparison function to a sort 
procedure, but the comparision function can't be stored into a global 
variable or returned from the enclosing function.

i.e. in Pascal you can do this...

procedure foo(var a : array [1..100] of integer)

   function less(l : integer, r : integer) : boolean
   begin
     less := a[l] < a[r]
   end

begin
   sort(a, less)
end;

...but you can't do return the "less" function from "foo", or store it 
into a global variable, and expect it to work afterwards.  Of course, in 
Pascal, one of the reasons you can't do this is that there's no syntax 
that allows it... :-)


if you're more interested in how the nuts and bolts of it work, the 
Dylan you showed migth be translated by a suitable compiler into the 
following C:

#include <stdio.h>

struct make_score_environment {
  int points;
};

struct make_score_closure {
  int (*func)(struct make_score_environment *env, int increase);
  struct make_score_environment env;
};

int make_score_anon(struct make_score_environment *env, int increase){
  return env->points += increase;
}

struct make_score_closure make_score(int points){
  struct make_score_closure result;
  result.env.points = points;
  result.func = make_score_anon;
  return result;
}


int main(){
  struct make_score_closure david = make_score(100);
  struct make_score_closure diane = make_score(400);
  printf("%d\n", (*david.func)(&david.env, 0));
  printf("%d\n", (*david.func)(&david.env, 10));
  printf("%d\n", (*david.func)(&david.env, 10));
  printf("%d\n", (*diane.func)(&diane.env, 10));
  printf("%d\n", (*david.func)(&david.env, 0));
  return 0;
}
Of course if you don't know C either then this isn't going to help much 
:-)

-- Bruce



Follow-Ups: References: