Home > Archive > Scheme > October 2006 > Re: Nice historical explanation of when and why term "closure" came
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 |
Re: Nice historical explanation of when and why term "closure" came
|
|
| Torben Ęgidius Mogensen 2006-09-19, 4:01 am |
| Joachim Durchholz <jo@durchholz.org> writes:
> Torben Ęgidius Mogensen schrieb:
>
> For a selection of closure values, you're restricted to those that
> exist in the source code.
That is also true for SML: Any closure is a pair of code from the
source code and an environment binding free variables.
> E.g. If you have a two-parameter function foo (x, y), you can't
> construct a single-parameter function foo (3, y) and pass that around.
> If you have two-parameter functions foo (x, y) and bar (z, a), you
> can't construct a three-parameter function foo (bar (z, a), y) and
> pass that around.
>
> I.e. what's missing are operators that can compose function values.
These limitations are due to not being able to return functional
values from functions, not from lack of being able to construct the
functions. For example, you can have a function that takes two
functions and a continuation and calls the continuation with the
composition of these functions as argument (with apologies for
possible minor syntactic inaccuracies):
function compose(function f(x:integer):integer,
function g(x:integer):integer,
function k(function h(x:integer):integer):integer):integer
function fog(x:integer):integer
begin
fog := f(g(x))
end;
begin
k(fog)
end;
Torben
| |
| Joachim Durchholz 2006-09-28, 8:02 am |
| Torben Ęgidius Mogensen schrieb:
> Joachim Durchholz <jo@durchholz.org> writes:
>
>
> That is also true for SML: Any closure is a pair of code from the
> source code and an environment binding free variables.
Yes, but the code need not be a function, it can be any expression. In
particular one that's inline in the normal flow of the logic.
> These limitations are due to not being able to return functional
> values from functions, not from lack of being able to construct the
> functions.
I agree with the first half of that.
I can see the view for the second part. My view of "value" is something
that can be passed to callers; in that sense, Pascal doesn't offer a way
to construct function values (well, it doesn't even really have them).
Anyway. I think we can agree that Pascal's higher-order function
facilities are far too restrictive to make it useful as a functional
programming language :-)
Regards,
Jo
| |
| Andreas Rossberg 2006-09-28, 7:02 pm |
| Joachim Durchholz wrote:
>
> Yes, but the code need not be a function, it can be any expression. In
> particular one that's inline in the normal flow of the logic.
Unless you specifically talk about lazy languages, that's simply wrong.
Neglecting realisations of toplevel decs ("main"), every closure and
every piece of compiled code corresponds to a function that is
explicitly written, syntactically as a function expression or
definition, in the source program. There is no code representing
anything else but explicit function bodies.
NB: I remember that I already had the very same discussion with you some
time ago. I hope you believe me this time. ;-)
Cheers,
- Andreas
| |
| Ray Dillinger 2006-09-28, 7:02 pm |
| Andreas Rossberg wrote:
> Joachim Durchholz wrote:
[color=darkred]
> Unless you specifically talk about lazy languages, that's simply wrong.
> Neglecting realisations of toplevel decs ("main"), every closure and
> every piece of compiled code corresponds to a function that is
> explicitly written, syntactically as a function expression or
> definition, in the source program. There is no code representing
> anything else but explicit function bodies.
I've been thinking as I read this thread about "opening" closures
and/or "reclosing" them under the bindings of a different environment
than that in which they were created.
I cannot think of a sane reason why anyone would do this in the
design of new working code, but it is interesting at least to
consider from the perspective of teaching in language-design, or
for building emulation layers for running one or more of LISP's
Elder Dialects.
Bear
| |
| Joachim Durchholz 2006-10-01, 4:02 am |
| Ray Dillinger schrieb:
> Andreas Rossberg wrote:
>
>
>
> I've been thinking as I read this thread about "opening" closures
> and/or "reclosing" them under the bindings of a different environment
> than that in which they were created.
>
> I cannot think of a sane reason why anyone would do this in the
> design of new working code,
There are reasons to do that. There are even seriously considered
proposals for Haskell to introduce parameters that are doing that kind
of stuff.
For example, if you want to have different kinds of error reporting, you
can either add an error_logger parameter to each and any function, or
you can allow the environment to define what error_logger means in some
context.
Essentially, it's simply an implicit parameter, similar to a global
variable. This technique doesn't scale beyond a handful of names or so,
or so, but that doesn't make it entirely unsuitable.
Regards,
Jo
| |
| Joachim Durchholz 2006-10-01, 8:02 am |
| Andreas Rossberg schrieb:
> Joachim Durchholz wrote:
>
> [...] every closure and
> every piece of compiled code corresponds to a function that is
> explicitly written, syntactically as a function expression or
> definition, in the source program. There is no code representing
> anything else but explicit function bodies.
>
> NB: I remember that I already had the very same discussion with you some
> time ago. I hope you believe me this time. ;-)
I'm not into beliefs ;-)
Actually I still disagree. I simply don't see how this differs from,
say, integer values, which in a sense correspond to integers written in
the program, too (every integer value started with a literal after all).
In other words, either I overlook some important difference between
integer and function values, or the argument doesn't hold up semantically.
Even at the implementation level, you can have code objects that weren't
written that way - even in non-lazy languages. For example, when
constructing a closure, some of the parameters of a function may be
filled with arguments; if function bodies are stored as DAGs, and the
function and arguments are pure, the run-time system could do partial
evaluation and arrive at a DAG that isn't a 1:1 translation of any code.
In fact the set of potential such DAGs in a program is infinite
(countably, I think, though it could even have Aleph-1 cardinality under
lazy evaluation).
If you don't have partial evaluation, your argument could still be
translated to values of sum and product types, amounting to "any sum or
product type is just a variant of a value that's written somewhere in
the source code".
I have a hunch that this kind of argument ignores the structure of
composite values. Whether the primitive parts of a composite values are
integers, constructor names, or functions: for a composite value, the
arrangement in which they were composed does have a massive influence on
its semantics.
Of course, for some argument, abstracting away the structure is entirely
valid, and for others, it is not. I'm not 100% sure what exactly you
mean to argue, so I'm not sure that it's valid in this case.
Regards,
Jo
| |
| Joachim Durchholz 2006-10-01, 7:05 pm |
| rossberg@ps.uni-sb.de schrieb:
> But then again, I don't see the point you are trying to make by this
> comparison.
Your point (as far as I have understood it) is that "there are no
computed function values, they are all just trivial compositions of
functions that you can see in the source".
My point in response to this is twofold:
At the *semantic* level, I think this point can be applied to all
values. Any value is some combination of unchanged primitives and
composition operators.
An example of this argument is integer arithmetic. You'd say that 4 is
created out of 3 by a program that increments it input by one; however,
semantically, the program is incrementing Succ Succ Succ Zero to
Succ Succ Succ Succ Zero, and both Succ and Zero are already in the
code. No magic and no creation of new values involved.
So functional values are no different from integers (or anything else
constructed from a finite number of primitive building blocks). Which
means that your argument, while technically true, doesn't describe
anything that distinguishes functions from other kinds of values.
At the *implementation* level, there can be differences.
There are programming language implementations that translate to formats
that can't easily be changed after the fact. Compilers that translate to
machine code are in that class by default (unless the CPU has a very
unusual instruction architecture, or the machine code comes with a lot
of tagging). Most bytecode architectures are in that class, too.
In such an environment, your view is correct. If a functional value is
passed around, the code that actually gets executed is never changed, it
is just combined in various interesting ways.
Then there are environments that translate to some variations of ASTs
(actually it's DAGs most of the time, since names are usually replaced
with links to the values that they represent). Haskell interpreters tend
to do this; actually one widely-used way of executing Haskell seems to
be a sequence of DAG transformation steps. Please note that this isn't
necessarily tied to lazy evaluation, it is (as far as I can see right
now) tied to partial evaluation, i.e. replacing a node in a DAG with a
shorter representation (i.e. replace code that evaluated to Zero with a
node contains a link to the memory cell that represents Zero, or similar
transformations).
In such an environment, you are not correct. Most functions will
transform into something that isn't directly recognizable from the
source code anymore, particularly after a few rounds of expanding and
partially evaluating recursive functions. Actually I think that
undecidability issues would make any mapping other than simulating the
evaluation process would be undecidable.
In other words, what I have understood of your point doesn't make sense
at the semantic level, and is just partially valid at the implementation
level.
Regards,
Jo
|
|
|
|
|