Home > Archive > Scheme > June 2004 > creating a procedure
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
creating a procedure
|
|
| Marlene Miller 2004-06-03, 7:27 pm |
| (define p (lambda (x) (+ x 1)))
When a lambda expression is evaluated, a procedure is created. In my little
interpreter for a school project, what happened was a tagged list (closure
(lambda(x) (+ x 1) <env> ) was created. So I guess, in general some structure
is created holding some the symbols that make up the parameters and the
expressions of the procedure body and also holding a pointer to an
environment structure.
In my everyday real world, it doesn't work that way. There is no such thing
as creating a procedure. A procedure is a sequence of instructions -
allocate memory on the stack, load values into the memory, then do some
loads and stores, tests and branches. When the OS evaluates my program, it
doesn't create a procedure.
SICP talks about the substitution model and the environment model of
evaluating an expression. By analogy, do we also say there is the
interpreter model and the compiler model of evaluating a program? Do we say
neither way is real, it's really just nand and nor gates wired together, or
p-stuff and n-stuff and electrical charges; so either model is valid when
thinking about how a program executes.
When I need to understand a program deeper than just reading the source
code, I look at what the compiler generates. When I think about how my
source is code is executing, I think about stack frames and instructions.
Now, what am I going to do with this new way of thinking about how an
interpreter evaluates expressions? Where does it fit in?
| |
| Marlene Miller 2004-06-03, 7:27 pm |
| correction - that thought about the interpreter model and the compiler model
evaluating a program is wrong. I recall a previous explanation about using
an interpreter to transform a machine and using a compiler to transform a
program.
| |
| Joe Marshall 2004-06-03, 7:27 pm |
| "Marlene Miller" <marlenemiller@worldnet.att.net> writes:
> (define p (lambda (x) (+ x 1)))
>
> When a lambda expression is evaluated, a procedure is created. In my little
> interpreter for a school project, what happened was a tagged list (closure
> (lambda(x) (+ x 1) <env> ) was created. So I guess, in general some structure
> is created holding some the symbols that make up the parameters and the
> expressions of the procedure body and also holding a pointer to an
> environment structure.
>
> In my everyday real world, it doesn't work that way.
It doesn't? It does for me.
> There is no such thing as creating a procedure.
Only in wimpy languages.
> A procedure is a sequence of instructions - allocate memory on the
> stack, load values into the memory, then do some loads and stores,
> tests and branches.
That's certainly a definition of procedure in some languages, but it
isn't the whole story.
> When the OS evaluates my program, it doesn't create a procedure.
Yes, it does.
> SICP talks about the substitution model and the environment model of
> evaluating an expression. By analogy, do we also say there is the
> interpreter model and the compiler model of evaluating a program? Do we say
> neither way is real, it's really just nand and nor gates wired together, or
> p-stuff and n-stuff and electrical charges; so either model is valid when
> thinking about how a program executes.
Different models work at different levels. It isn't very enlightening
to think about higher-order procedures when you have a soldering iron
in your hand. Likewise, it is useless to think about wires and gates
when you are typing a class specification. But they are both models
of computation.
> When I need to understand a program deeper than just reading the source
> code, I look at what the compiler generates. When I think about how my
> source is code is executing, I think about stack frames and instructions.
>
> Now, what am I going to do with this new way of thinking about how an
> interpreter evaluates expressions? Where does it fit in?
It fits in the suitcase with the other models.
| |
| Marlene Miller 2004-06-03, 7:27 pm |
| Thank you Joe. I am having difficulty trying to find a way to articulate my
confusion. Let me try a different approach.
The OS is an interpreter that evaluates my program. So I write a big C++
program, compile it, and the OS executes it. I could imagine the OS behaving
something like the interpreters I am learning about. Perhaps the
machine-level instructions are expressions to be evaluated?
So if I learn about interpreters, I will understand how the OS executes my
big C++ program? But it won't help me to understand the C++ language,
because that is just data for the OS interpreter?
If you understand how a language could be evaluated, then do you understand
the language? If that is true, that is what I do not understand. Or do you
understand a language by understanding how it could be compiled? That is
what I know.
| |
| Lauri Alanko 2004-06-03, 7:27 pm |
| In article <UKMsc.27347$fF3.702566@bgtnsc05-news.ops.worldnet.att.net>,
Marlene Miller <marlenemiller@worldnet.att.net> wrote:
> (define p (lambda (x) (+ x 1)))
>
> When a lambda expression is evaluated, a procedure is created. In my little
> interpreter for a school project, what happened was a tagged list (closure
> (lambda(x) (+ x 1) <env> ) was created. So I guess, in general some structure
> is created holding some the symbols that make up the parameters and the
> expressions of the procedure body and also holding a pointer to an
> environment structure.
Possibly. That is not the only way to do it, but it is common.
Often, though, the structure holds a pointer to some form of internal
"compiled" code instead of pure verbatim scheme expressions.
> In my everyday real world, it doesn't work that way. There is no such thing
> as creating a procedure. A procedure is a sequence of instructions -
> allocate memory on the stack, load values into the memory, then do some
> loads and stores, tests and branches. When the OS evaluates my program, it
> doesn't create a procedure.
Instead of "OS" you should here perhaps rather speak of the "platform"
or "architecture". In this case, the _execution_ is done by the
underlying hardware (or something that emulates it), while the
operating system provides just the run-time: system calls, scheduling,
etc.
In any case it most certainly is possible to create machine-code
procedures at run-time. This is what dynamic loaders and just-in-time
compilers do. In addition, it is one way to expose first-class
functions in a functional language to C. Here's an example in Haskell:
<http://www.cse.unsw.edu.au/~chak/ha...st/fexport.html>
(The dynamic export bit. It is quite rightly called "gruesome" and
"horrible hack", but it works.)
> SICP talks about the substitution model and the environment model of
> evaluating an expression. By analogy, do we also say there is the
> interpreter model and the compiler model of evaluating a program? Do we say
> neither way is real, it's really just nand and nor gates wired together, or
> p-stuff and n-stuff and electrical charges; so either model is valid when
> thinking about how a program executes.
Substitution and environments are different ways of conceptualizing
and formalizing the semantics of a programming language. But they are
also different practical implementation techniques. Substitution means
that whenever a procedure is applied, a new version of its body is
generated with the formal parameters replaced by the arguments. This
can also be done with machine code, although it gets very hairy.
Substitution isn't really used much as an implementation technique
because it is inefficient: every application requires the
transformation of the entire body of the procedure, which consumes
both time and space.
On the other hand, the code resulting from the substitution is then
"inlined" and in some situations perhaps more efficient than ordinary
code that would need to look up variables from environments. But this
is used more in a special technique called partial evaluation, rather
then in ordinary procedure applications.
(Of course all these notions are abstractions built on top of (our
perceptions of) physical phenomena, but thinking too deeply about that
will just divert you from the central issues here.)
> When I need to understand a program deeper than just reading the source
> code, I look at what the compiler generates. When I think about how my
> source is code is executing, I think about stack frames and instructions.
>
> Now, what am I going to do with this new way of thinking about how an
> interpreter evaluates expressions? Where does it fit in?
There is no single way in which a scheme program is executed at
machine code level. It all depends on the implementation. But usually
things are done more or less by the environment model, as described in
SICP.
Btw, when you want to edit your article, you can "cancel" or
"supersede" the old one instead of simply sending a new updated
article.
HTH.
Lauri Alanko
la@iki.fi
| |
| Marlene Miller 2004-06-03, 7:27 pm |
| Thank you, Lauri.
| |
| Marlene Miller 2004-06-03, 7:27 pm |
| I think I am going to have to read SICP chapter 5 :-( :-)
I stopped at chapter 4, because the class ended.
| |
| Ray Dillinger 2004-06-03, 7:27 pm |
| Marlene Miller wrote:
> Thank you Joe. I am having difficulty trying to find a way to articulate my
> confusion. Let me try a different approach.
>
> The OS is an interpreter that evaluates my program. So I write a big C++
> program, compile it, and the OS executes it. I could imagine the OS behaving
> something like the interpreters I am learning about. Perhaps the
> machine-level instructions are expressions to be evaluated?
What an expression is varies, depending on the language. In
machine language, what you have is a current state of the program
held in memory, and execution proceeds by performing instructions
that modify that state. You can think of these instructions as
expressions if you want, but they're not "pure" expressions -
they have side effects and frequently "implicit" arguments that
aren't obvious. Indeed, most of them work specifically by
having side effects.
The operating system is a program itself; bits of it work like
an interpreter for "complex" instructions, either to get very
repetitive code out of the programs or to make a trustable
way for programs to access something that programs can't be
trusted with directly (like writing on the screen -- you can't
let one program run the whole screen when the user has six
different programs open, so part of your operating system called
the "window manager" acts as an interpreter for commands from
programs that do things with their windows).
But most of your compiled program, in C++, runs directly on the
hardware. When the Operating system gives over control to the
program, the machine follows an ancient hardware-driven cycle
of instruction fetch, instruction execute, repeat, directly
grabbing the bytes of the program out of the memory and presenting
them on the hardware instruction inputs of the chip. It makes a
lot of sense to regard a CPU as a hardware implementation of an
interpreter.
The operating system takes control back when the program that's
running executes a special instruction, or when a timer goes off
resulting in a hardware interrupt that causes the CPU to start
fetching instructions from the Operating system's instruction
stream again.
Now, what an *interpreter* is, is a program that accepts
instructions in some language, usually instructions that
don't contain any actual machine code at all, and uses them
to direct its own action. Bytecode interpreters are a fairly
common way to implement scheme; the scheme program code is
compiled (transformed) into a stream of bytes which are stored
in memory. Now the CPU is doing fetch, execute, on the code
of the interpreter, and the effect of its running the
interpreter's instructions is to simulate the a different
sort of CPU doing fetch, execute, on the bytecodes.
Direct interpreters (that work directly on program code instead
of bytecodes) are possible too, but nowhere near as efficient,
because they have to do a lot more work string handling and
parsing the program code.
> So if I learn about interpreters, I will understand how the OS executes my
> big C++ program? But it won't help me to understand the C++ language,
> because that is just data for the OS interpreter?
C++ code is data for a different program, called a "compiler."
The job of a compiler is to take one form of a program (usually
a high-level language like c++ or Scheme) and produce a different
form (like bytecode or machine language instructions). Depending
on what the compiler produces, its output runs either on the
hardware, or is used as input data for an interpreter which runs
on the hardware. Whichever program runs on the hardware calls
special routines inside the operating system whenever it needs
to do anything besides read and write memory it's already been
allocated.
> If you understand how a language could be evaluated, then do you understand
> the language? If that is true, that is what I do not understand. Or do you
> understand a language by understanding how it could be compiled? That is
> what I know.
There are many different words that are spelled "understand."
Without knowing which one you mean, it is hard to answer your
question. An understanding of the way the language is evaluated
is a key tool in understanding the language itself. But there
are different levels of understanding, where you go beyond how
a function can be implemented and understand what it's good for
and why it's better this way than that. And understanding how
it's evaluated is only the first step on that path.
I hope this helps.
Bear
| |
| Steven T Abell 2004-06-03, 7:27 pm |
| Marlene Miller wrote:
> (define p (lambda (x) (+ x 1)))
>
> When a lambda expression is evaluated, a procedure is created.
>
> In my everyday real world, it doesn't work that way.
Part of the general Lisp experience
is learning that what you thought was computing
is only a little part of it.
As a very crude example,
one of the current tricks in the Java community
is to write a Java program that recognizes some situation,
then writes an adjunct to itself
that it compiles, loads, and runs subordinate to itself.
Given Java's complex syntax, this isn't a lot of fun,
but it makes it possible to do some things
that are outside the scope of "normal" programming.
IOW for some problems, you can't write a program
that can handle all the situations that might arise,
but you might be able to write a program
that writes programs that can.
In Lisp-like languages, this is not considered an exotic practice.
Right now, I'm working on a Scheme program for a client
who needs to generate a lot of fairly complex C structs.
My code doesn't generate the structs,
it generates procedures that generate the structs.
It turns out to be simpler to do it that way
using modes of thinking that look pretty strange to C programmers
mostly because their language won't let them think like that.
Remember Hamlet's line:
"There are more things in heaven and earth, Horatio,
than are dreamt of in your philosophy."
Have fun with it.
Steve
| |
| Marlene Miller 2004-06-03, 7:27 pm |
| Thank you very much Ray-Bear for your detailed explanations. Thank you for
your time.
Is it reasonable to apply the model of an interpreter to the hardware when
it evaluates a program? When the hardware evaluates the binary data from a
compiled C++ function definition, can we say the hardware creates a closure
and adds a frame to an environment structure?
"An understanding of the way the language is evaluated is a key tool in
understanding the language itself."
Would you please explain?
| |
| Marlene Miller 2004-06-03, 7:27 pm |
| Thank you Steve for your insights.
> Part of the general Lisp experience
> is learning that what you thought was computing
> is only a little part of it.
I've noticed that some programming language courses use Scott's Programming
Language Pragmatics whereas others use Friedman's Essentials of Programming
Languages. The first book is what I am used to. The second book is unusual.
I am trying to figure out where SICP and EOPL fit in amongst all the other
stuff I need to learn. They are fun to study. But they seem like a world
unto themselves.
| |
| Neil W. Van Dyke 2004-06-03, 7:27 pm |
| Steven T Abell <newsgroup@brising.com> writes:
> Remember Hamlet's line:
> "There are more things in heaven and earth, Horatio,
> than are dreamt of in your philosophy."
By some accounts, the original was, "...our philosophies,"
which I think is a stronger and more useful statement,
both for Hamlet and for Scheme practitioners. :)
| |
| Anton van Straaten 2004-06-03, 7:27 pm |
| Marlene Miller wrote:
> Thank you Steve for your insights.
>
>
> I've noticed that some programming language courses use Scott's
Programming
> Language Pragmatics whereas others use Friedman's Essentials of
Programming
> Languages. The first book is what I am used to. The second book is
unusual.
> I am trying to figure out where SICP and EOPL fit in amongst all the other
> stuff I need to learn. They are fun to study. But they seem like a world
> unto themselves.
As various people have hinted, the world that Scheme and Lisp encompass is
larger and more general, in some important ways, than that of languages like
C++ or Java. Those "pragmatic" languages are subsets. For example, the
reason you can't find a correlate for "creating a procedure" at runtime in
C++ is because C++ doesn't support that capability directly.
But that doesn't mean you don't need that functionality in C++. Things like
the "functor pattern" are specifically designed to make up for C++'s lack of
the ability to create procedures at runtime. But they require much more
manual work, and are more error-prone. This is the sort of thing that leads
to Greenspun's Tenth Rule.
The reason the material surrounding languages like Scheme seem like a world
unto themselves is because they derive their design from a mathematical
model of programming, whereas it sounds as though you're more used to a
hardware-oriented model. Languages like C++ deliberately avoid abstracting
the underlying hardware too much: they're designed to allow programmers to
control the machine code which their programs generate to a degree that, 98%
of the time, is unnecessary. The cost for this is that there's a limit to
the level of abstraction C++ programs can achieve - you can't write working
C++ programs without being aware of the underlying hardware model. Picking
a hardware model as a model to "think with" is seriously limiting.
Languages like Scheme make little concession to hardware: they're designed
to do support the semantics you need to design sophisticated programs, not
the semantics imposed by a machine's quirks and limitations.
That doesn't mean Scheme has to be inefficient or impractical, though. Not
surprisingly, approaching things from a mathematical perspective has some
big benefits, and generating efficient compiled code from languages like
Scheme or Lisp is quite feasible.
But much more importantly than the specifics of what particular language
implementations do or don't do, is the idea that greater understanding is
achieved from more general models, which can be applied specifically in a
great variety of situations. That's what the academic discipline behind
languages like Scheme is about. C++ functions are just one particular way
of implementing a subset of the much more general notion of procedure which
Scheme defines.
Anton
| |
| Joe Marshall 2004-06-03, 7:27 pm |
| "Marlene Miller" <marlenemiller@worldnet.att.net> writes:
> If you understand how a language could be evaluated, then do you
> understand the language? If that is true, that is what I do not
> understand. Or do you understand a language by understanding how it
> could be compiled? That is what I know.
I'd have to counter with the question `what does it mean to understand
a language?' Many people `understand' Visual Basic and have no idea
at all how it is interpreted or compiled. Researchers write
experimental interpreters and compilers in an effort to `understand'
language constructs, in effect, they understand the interpreter but
not the language. But if the reason that they write the interpreter
is to `understand' the language, then clearly they believe that
understanding the evaluation model can be a very important step in
understanding the language.
An interpreter is a program. A program is a formal description of a
process. If you understand the interpreter, you will understand
one possible process of executing code in the target language. While
there may be other possible processes (for instance MIT Scheme and
PLT Scheme use different underlying models) the major features of any
process that executes the code will be similar.
I've written a number of interpreters and compilers for many different
languages. I can *guarantee* that if you write an interpreter for a
language that you will learn a tremendous amount about the language.
--
~jrm
| |
| Joe Marshall 2004-06-03, 7:27 pm |
| "Marlene Miller" <marlenemiller@worldnet.att.net> writes:
> Is it reasonable to apply the model of an interpreter to the hardware when
> it evaluates a program?
Very much so.
> When the hardware evaluates the binary data from a compiled C++
> function definition, can we say the hardware creates a closure and
> adds a frame to an environment structure?
No, but that's because C++ functions don't have an environment.
Hardware is usually very limited in what operations it can perform,
so creating a closure might not be a native instruction. In addition,
hardware is usually designed by hardware engineers that are not
trained in high-level computer languages. They tend to think about
hardware in terms of bits, bytes, registers, stacks, etc. rather than
in terms of closures, variable references, environments, etc. The
typical machine code manual will be written in terms that don't seem
to have much to do with high-level constructs.
For instance, the hardware manual will talk about pushing return
addresses and adjusting frame pointers, but this can be understood as
allocating and initializing a continuation. The hardware manual will
talk about condition codes, but this can be understood as the return
value of certain built in predicates (and because it is a true/false
value it is implemented as a single bit). All the computations can be
thought of as a primitive APPLY. There is no way to extend the
language (without a soldering iron), so you only get the primitives.
As an exercise, in your copious free time (heh heh heh), take a look
at http://home.comcast.net/~prunesquallor/kmachine.htm and just skim
the text. Don't worry about getting a deep understanding. Just look
at the call hardware description. You should see that there is a
fairly straightforward mapping from lisp expressions to machine
expressions. We designed the hardware to interpret Lisp as directly
as was feasible. If you understand how a Lisp interpreter works, it
should be fairly easy to see what we were trying to accomplish. (Of
course hardware adds a myriad of its own constraints having to do with
voltages, signal transmission, economy of parts, etc.)
--
~jrm
| |
| Joe Marshall 2004-06-03, 7:27 pm |
| "Marlene Miller" <marlenemiller@worldnet.att.net> writes:
> I think I am going to have to read SICP chapter 5 :-( :-)
Definitely. It has a number of the answers you are looking for.
> I stopped at chapter 4, because the class ended.
Ouch!
--
~jrm
| |
| Joe Marshall 2004-06-03, 7:27 pm |
| Steven T Abell <newsgroup@brising.com> writes:
> Right now, I'm working on a Scheme program for a client who needs to
> generate a lot of fairly complex C structs. My code doesn't
> generate the structs, it generates procedures that generate the
> structs. It turns out to be simpler to do it that way
I did that a few months ago. The C language does not make it easy to
create and initialize structures, but it is fairly easy to write
nested functions that do it. I ended up with a ton of code like this:
Object lambda_body_132 = make_sequence2 (sequence_first_134, sequence_second_133);
Object operator_130 = make_auxlambda (0, 54, lambda_body_132);
Object operator_1931 = make_variable (Symbol_127);
Object argument_1932 = make_variable (Symbol_100);
Object operator_1934 = make_primitive (Symbol_1936);
Object argument_1935 = Symbol_1937;
Object argument_1933 = make_combination1 (operator_1934, argument_1935);
Object argument_131 = make_combination2 (operator_1931, argument_1933, argument_1932);
Object sequence_first_129 = make_combination1 (operator_130, argument_131);
Object operator_1940 = make_variable (Symbol_988);
Object argument_1941 = make_variable (Symbol_1030);
Object argument_1942 = Symbol_1944;
Object argument_1943 = make_variable (Symbol_1945);
Object sequence_first_1939 = make_combination3 (operator_1940, argument_1943, argument_1942, argument_1941);
--
~jrm
| |
| Ray Dillinger 2004-06-03, 7:27 pm |
| Marlene Miller wrote:
> Thank you very much Ray-Bear for your detailed explanations. Thank you for
> your time.
>
> Is it reasonable to apply the model of an interpreter to the hardware when
> it evaluates a program?
Yes, but there's a small terminology issue. "Evaluate an
expression" means "find the value that this expression
generates." What hardware does is more general. It
"executes statements", which means it "does what this
command says to do." A statement may or may not generate
a value, but an expression always does.
Languages like C and C++ have statements; languages like
scheme have expressions. Every "statement" in scheme
returns a value, even if sometimes the value is unspecified
or not important.
> When the hardware evaluates the binary data from a
> compiled C++ function definition, can we say the hardware creates a closure
> and adds a frame to an environment structure?
It's fairly primitive, but yes. The "frame" that's created
is created by changing the stack pointer register so that the
stack now occupies more memory. The stack is the "environment
structure" of a C/C++ program, and the area that's added to the
stack becomes the new "frame". The "closure", if one exists,
is a combination of arguments written into the new frame and
persistent arguments that are stored elsewhere.
The way C and C++ handle their invocation frames is fast and
simple, but it doesn't allow them to do some of the things
that a more complicated environment structure can handle,
like using general continuations.
> "An understanding of the way the language is evaluated is a key tool in
> understanding the language itself."
> Would you please explain?
It depends on what it is about the language that you want
to understand. Understanding how a language is evaluated
(or executed) will help you know what is efficient to do
in that language and understand what the hardware is doing
in response to your expressions (or statements).
But it will not, by itself, mean that you fully understand
how the different parts of the language work together and
how the constructions and structures and idioms allow people
to express complex ideas in simple and efficient ways. Lots
of people who "understand" advanced languages, in principle
or in execution method, still don't understand how to use
them to save themselves work, because their minds run in
the habitual paths they learned with other languages and
don't see the value of (for example) creating procedures at
runtime.
On the other hand, I've encountered programs written in C
that had hairy machine-language procedures that write machine
code instructions into buffers, flush the buffer cache,
directly change the stack pointer to allocate a frame, and
then jump to the first address in the buffer, doing an end-
run around this particular limitation of C. The guys who
write stuff like that clearly "get it" about the value of
creating new procedures at runtime, but they're doing it
in a way that is an absolutely huge amount of work and a
maintenance/portability nightmare as compared to the
facilities found in scheme or LISP.
Bear
| |
| Joe Marshall 2004-06-03, 7:27 pm |
| Ray Dillinger <bear@sonic.net> writes:
> Yes, but there's a small terminology issue. "Evaluate an
> expression" means "find the value that this expression
> generates." What hardware does is more general. It
> "executes statements", which means it "does what this
> command says to do." A statement may or may not generate
> a value, but an expression always does.
In some hardware, most notably `RISC' hardware, every instruction
*does* generate a value and you have to do something with it. The
usual practice is to designate a particular location as a `bit bucket'
an send the value there if you don't want it.
Discarding values is going to increase the entropy in the machine, so
some have suggested never discarding values but running the processor
backwards at the end of a computation. This seemingly absurd idea
actually reduces power consumption and heat generation.
--
~jrm
| |
| Joe Marshall 2004-06-03, 7:27 pm |
| "Marlene Miller" <marlenemiller@worldnet.att.net> writes:
> I've noticed that some programming language courses use Scott's Programming
> Language Pragmatics whereas others use Friedman's Essentials of Programming
> Languages. The first book is what I am used to. The second book is unusual.
> I am trying to figure out where SICP and EOPL fit in amongst all the other
> stuff I need to learn. They are fun to study. But they seem like a world
> unto themselves.
SICP and EOPL subsume the rest.
In many disciplines there are different levels of understanding.
Metallurgists and welders both work with metal, potters and material
engineers may both work with clay, automotive engineers and truck
drivers both work with vehicles. The same is true with computers.
There are a lot of courses and books that teach you `how to program'
in a particular language or language family. There are few that teach
you `how to think about computation'. But if you learn the latter the
former is rather pointless. A large number of people in this group
have *never* taken a single `programming language' course, yet they
can take a language they have never before seen and write code that is
clearly above the level taught in any `advanced course' in that
language.
--
~jrm
| |
| Marlene Miller 2004-06-03, 7:27 pm |
| I'd like to thank you, Joe, Ray-Bear, Lauri, Steve, Anton, Neil and
Matthias, for your interesting and detailed responses to my questions and
confusion.
First I'd like to thank you for your Time, because it takes time to write
something clearly and in detail. And it takes time to read through the
thread, follow where it is going, figure out what the problem is and think
about what you can offer and how you want to express your ideas.
I'd like to thank you for working with me even though I was not able to
present a nice, clear specific question.
I want you to know even though I may not have responded (yet) to your
comments, I am reading and thinking and trying to understand what each of
you has said. There is a lot to think about and sort out.
| |
| Ray Dillinger 2004-06-03, 7:27 pm |
| Joe Marshall wrote:
> "Marlene Miller" <marlenemiller@worldnet.att.net> writes:
>
>
> No, but that's because C++ functions don't have an environment.
With few exceptions *all* functions must have an environment.
There has to be a way for any ordinary function to resolve
variable names and access values. In C++, it just happens to
be a pathetically limited environment called the stack.
Bear
|
|
|
|
|