Returning closures

Started by eliben, December 05, 2006, 11:31:56 AM

Previous topic - Next topic

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:



(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:



(setq doubler (make-multipler 2))
(funcall doubler 13)


Produces 26.

And then:



(setq tripler (make-multipler 3))
(funcall tripler 13)


Produces 39.



How can I do it in newlisp ?



Thanks in advance

Lutz

#1
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:



>  (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:





(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:



(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:



(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

Fanda

#2
I tried to implement closures outside of contexts. This version uses pair of functions:

http://newlisp-on-noodles.org/wiki/index.php/Functional_Programming#Closures">http://newlisp-on-noodles.org/wiki/inde ... g#Closures">http://newlisp-on-noodles.org/wiki/index.php/Functional_Programming#Closures


> (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:
> (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

#3
here is a closure-like multiplier using contexts:


(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:
> (multiplier:make 'double 2)
> (multiplier:make 'triple 3)

> (double 13)
26
> (triple 13)
39
>


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



Lutz

ino-news

#4
Quote from: "Lutz"here is a closure-like multiplier using contexts:


(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

#5
Not sure what you mean, it works for me:



~> 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

#6
Quote from: "Lutz"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

(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

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


(context 'multiplier)

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

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

(context 'MAIN)


When calling:


(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:


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.