Returning closures

For the Compleat Fan
Locked
eliben
Posts: 11
Joined: Wed Apr 19, 2006 8:54 pm

Returning closures

Post by eliben »

Hello,

I'm trying to understand how to utilize the contexts of newlisp to implement various lexical bound idioms, for instance returning closures. Take this CL code for example:

Code: Select all

(defun make-multiplier (x) (lambda (y) (* x y))
make-multiplier is a generator of multiplier functions. It can generate as many functions as I want, each with its own internal data. For example:

Code: Select all

(setq doubler (make-multipler 2))
(funcall doubler 13)
Produces 26.
And then:

Code: Select all

(setq tripler (make-multipler 3))
(funcall tripler 13)
Produces 39.

How can I do it in newlisp ?

Thanks in advance

Lutz
Posts: 5289
Joined: Thu Sep 26, 2002 4:45 pm
Location: Pasadena, California
Contact:

Post by Lutz »

There are really two very distinct answers to your question. First let me show how to rewrite your 'make-multiplier' example in newLISP in an interactive console session:

Code: Select all

>  (define (make-multiplier x) (expand (fn (m) (* x m)) 'x))
(lambda (x) (expand (lambda (m) (* x m)) 'x))

> (define doubler (make-multiplier 2))
(lambda (m) (* 2 m))

> (doubler 13)
26

> (define tripler (make-multiplier 3))
(lambda (m) (* 3 m))

> (tripler 13)
39
>


The second answer:

There is now such thing as closures in newLISP, and I would not try simulating closures in newLISP. To code state-keeping functions newLISP uses contexts (namespaces).

Here is a quick crash course in to coding state full functions in newLISP:

Code: Select all

(context 'gen)

(define (foo)
    (if (number? acc)
        (inc 'acc)
        (set 'acc 0)))

(context MAIN)

> (gen:foo)
0
> (gen:foo)
1
> (gen:foo)
2
> (gen:foo)
3
This way to write state keeping functions is good for bigger code size and when there are several function in the same namespace.

When just writing a little snippet there is a shorter way without bracing it with context switches:

Code: Select all

(define (gen:foo)
    (if (number? gen:acc)
        (inc 'gen:acc)
        (set 'gen:acc 0)))

> (gen:foo)
0
> (gen:foo)
1
> (gen:foo)
2
> (gen:foo)
3
A function inside a namespace with the same name as the namspace is called a default function. Default functions can be called just using the name space name:

Code: Select all

(define (gen:gen)
    (if (number? gen:acc)
        (inc 'gen:acc)
        (set 'gen:acc 0)))

> (gen)
0
> (gen)
1
> (gen)
2
> (save "gen.lsp" 'gen) ; now look at the file gen.lsp !
The last line shows how a function and its state can be easily serialized to a file.

newLISP is a 'new' function to duplicate/copy namespace objects and a 'def-new' to duplicate just portions for mixins. You should read the relevant chapters in the manual:

Chapter 16 (read this first):

file:///usr/share/newlisp/doc/newlisp_manual.html#context_objects

Chapter 15:

file:///usr/share/newlisp/doc/newlisp_manual.html#contexts

Lutz
Last edited by Lutz on Wed Dec 06, 2006 2:33 am, edited 1 time in total.

Fanda
Posts: 253
Joined: Tue Aug 02, 2005 6:40 am
Contact:

Post by Fanda »

I tried to implement closures outside of contexts. This version uses pair of functions:
http://newlisp-on-noodles.org/wiki/inde ... g#Closures

Code: Select all

 > (define-closure (multiplier x) (c 2) (* c x))
(lambda () 
 (let (-result- (apply _multiplier_ (args))) 
  (update-closure '_multiplier_ (last -result-)) 
  (first -result-)))

> _multiplier_
(lambda (x) 
 (let (c 2) 
  (list (* c x) (apply append (map list '(c) (map eval '(c)))))))

> (multiplier 13)
26
'closure-set' is changing internal state of helpful functions:

Code: Select all

> (closure-set 'multiplier 'c 3)
3

> _multiplier_
(lambda (x) 
 (let (c 3) 
  (list (* c x) (apply append (map list '(c) (map eval '(c)))))))

> (multiplier 13)
39
Comments welcome, Fanda

Lutz
Posts: 5289
Joined: Thu Sep 26, 2002 4:45 pm
Location: Pasadena, California
Contact:

Post by Lutz »

here is a closure-like multiplier using contexts:

Code: Select all

(context 'multiplier)

(define (multiplier:multiplier x) (* x multi))

(define (multiplier:make ctx multi)
        (def-new 'multiplier:multiplier (sym ctx ctx)))

(context 'MAIN)
try in an interactive session:

Code: Select all

> (multiplier:make 'double 2)
> (multiplier:make 'triple 3)

> (double 13)
26
> (triple 13)
39
> 
save the 'double' object:

Code: Select all

(save "double.lsp" 'double)
to see what 'def-new' did

Lutz

ino-news
Posts: 17
Joined: Sat Jan 13, 2007 3:20 am
Location: germany

Post by ino-news »

Lutz wrote:here is a closure-like multiplier using contexts:

Code: Select all

(context 'multiplier)

(define (multiplier:multiplier x) (* x multi))

(define (multiplier:make ctx multi)
        (def-new 'multiplier:multiplier (sym ctx ctx)))

(context 'MAIN)
there's something wrong here: multiplier:make doesn't use argument
"multi", which should be saved in the multiplier context. otherwise, the
evaluation of (double some-number) breaks: "value expected in function *
: multi".

clemens
regards, clemens

Lutz
Posts: 5289
Joined: Thu Sep 26, 2002 4:45 pm
Location: Pasadena, California
Contact:

Post by Lutz »

Not sure what you mean, it works for me:

Code: Select all

~> cat multiplier 
(context 'multiplier) 

(define (multiplier:multiplier x) (* x multi)) 

(define (multiplier:make ctx multi) 
        (def-new 'multiplier:multiplier (sym ctx ctx))) 

(context 'MAIN) 

~> newlisp multiplier 
newLISP v.9.1.0 on OSX UTF-8, execute 'newlisp -h' for more info.

> (multiplier:make 'double 2)
double:double
> double:multi
2
> (save "double.lsp" 'double)
true
> !cat double.lsp 

(context 'double)

(define (double:double x)
  (* x multi))

(set 'multi 2)


(context 'MAIN)

> (double 13)
26
> 
what exactly did you do? Can you paste the session?

Lutz

ino-news
Posts: 17
Joined: Sat Jan 13, 2007 3:20 am
Location: germany

Post by ino-news »

Lutz wrote:Not sure what you mean, it works for me:

> (multiplier:make 'double 2)
double:double
> double:multi
2
> (save "double.lsp" 'double)
true
> !cat double.lsp

Code: Select all

(context 'double)

(define (double:double x)
  (* x multi))

(set 'multi 2)


(context 'MAIN)
what exactly did you do? Can you paste the session?
i don't have it anymore, but i remember that something went wrong.
i pasted the original code. newlisp throws an error when it meets
unbalanced paranthesis. so i "repaired" the multiplier definition, but
i was back in context MAIN then, due to my error.

what i didn't know was the ability to use shell code on newlisps command
line! and what i still don't understand it how the symbol 'multi gets
set in the cloned context 'double without beeing explicitly set in the
original 'multiplier. can you explain this to me? i only see it used
there. --clemens
regards, clemens

Lutz
Posts: 5289
Joined: Thu Sep 26, 2002 4:45 pm
Location: Pasadena, California
Contact:

Post by Lutz »

... how the symbol 'multi gets set in the cloned context 'double without beeing explicitly set in the original 'multiplier.

Code: Select all

(context 'multiplier)

(define (multiplier:multiplier x) (* x multi))

(define (multiplier:make ctx multi)
        (def-new 'multiplier:multiplier (sym ctx ctx)))

(context 'MAIN)
When calling:

Code: Select all

(multiplier:make 'double 2)
'def-new' will create a new function and context double:double as a copy of multiplier:multiplier. When 'def-new' (and 'new' too) copies variables they will also copy the bindings if the variable does not already exist in the target context. In this case multiplier:multi is bound to 2.

Remember that in newLISP inside the name-space/context the rules of dynamic scoping work. The variable multiplier:multi has closure via the name-space but not via the function 'multiplier:make' (as would be in Scheme). So when copying multiplier:multiplier to double:double than double:multi gets bound to the contents of multiplier:multi which is 2, because we are inside the (multiplier:make 'double 2) during the call.

Lutz

Ps: in the simple case of the 'multiplier' a better function factory would be:

Code: Select all

newLISP v.9.1.0 on OSX UTF-8, execute 'newlisp -h' for more info.

> (define double (curry * 2))
(lambda (_x) (* 2 _x))
> (double 13)
26
> 
of course 'curry' will only work for functions with 2 arguments where the 1st argument gets curried, else the above or a solution with 'letex' or 'expand' are more practical.

Locked