Return Styles: Pseud0ch, Terminal, Valhalla, NES, Geocities, Blue Moon. Entire thread

PROGRAMMING CHALLENGE

Name: Anonymous 2010-07-16 18:57


This one should be difficult enough for you guys,

Write a function foo that takes a number n and returns a function that takes a number i, and returns n incremented by i.


My submission, in Scheme


(define (foo num)
  (lambda (x) (+ x num)))

Name: Anonymous 2010-07-19 7:12

While I'm sure most of us love entertaining dynamic code generation, in reality, very few language implementations recompile the whole function to generate a closure (I haven't actually see any that do), instead, what they do is compile the closed over functions, and the closure state is referenced either through the stack(arguments) or through some other hidden way to pass arguments. It's also quite common to pass a reference instead of the literate value, this allows sharing the value between functions/closures. Closure objects themselves are very lightweight and only contain: tag bits (or some other way to identify them), a reference(pointer) to the function object (usually tagged as well), the closure data(array or structure(which are not that different from arrays)). Closures themselves are callable objects, just like functions (those functions objects) or other data types which can be callable (for example, funcallable instances(of classes) exist in CL implementations with MOP support, they're functionally equivalent to closures, but allow more fine-grained control)). Calling such an object would lead to either have the compiler generate the needed calling code by itself (if it's possible to determine at compile time), or call a dispatcher which performs the call at runtime depending on the type.

It should also be noticed that some of the closure implementations in this thread use literals, so they do not allow sharing state, here's a simple example which illustrates this as well as shows how the closure internals work:


(let ((a 0))
  (defun set-a (n)
    (setf a n))
  (defun get-a ()
    a))

CL-USER> (get-a)
0
CL-USER> (set-a 100)
100
CL-USER> (get-a)
100

and some internals:
#'get-a:

#<FUNCTION {24E2D435}>
--------------------
FUNCTION: #<FUNCTION GET-A {24DE7E05}>
Closed over values:
0: #<value cell 100 {24DE7C4F}>

#'set-a:

#<FUNCTION {24DE7C3D}>
--------------------
FUNCTION: #<FUNCTION SET-A {24DE7E3D}>
Closed over values:
0: #<value cell 100 {24DE7C4F}> <-- same pointer!

the value cell a:

#<SB-KERNEL::RANDOM-CLASS {24DE7C4F}>
--------------------
VALUE: 100

(In this case, the compiler actually compiles both functions at once, so they actually point to different offsets within the same code block, but that'd be too much detail to show here...)

Also keep in mind that while this used functions, closures can be generated at runtime quite easily and cheaply (just return a lambda), so whatever closure implementation your language supports needs to allow this as well (without the previous requirement, you can just use globals to achieve the same).

So the points I'm trying to make is that real closures:
1) Allow their value to be shared by other closures or pieces of code. They can close over anything in the lexical scope. Sharing values also means that if the language allows mutation of variables, this should also be supported.
2) Can be implemented fairly cheaply(in terms of speed and space cost) without the need of runtime code generation.

Sadly, I don't see how you can achieve these goals in ANSI C portably without adding your own function call macro, which would make everything quite clunky, also memory management would be a pain.

Newer Posts
Don't change these.
Name: Email:
Entire Thread Thread List