Controlling function scope

Started by Jeff, November 23, 2007, 01:17:05 PM

Previous topic - Next topic

Jeff

Is there a way to control the context in which code is evaluated at runtime?  For example, let's say I have this context:


(context 'foo)
(set 'a 10)
(context MAIN)


If I have a function, (fn () (println a)), is there a way I can apply it in the context of foo?



In Javascript, you can do some_function.apply(foo, args), which is the same as:


foo.fn = some_function;
foo.fn(args);
delete foo.fn;


From within the function call, the 'this' keyword would then refer to the scope 'foo'.  Can this be done in newLISP?
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code

cormullion

#1
I'm not too sure what you mean, Jeff. My reading of your question, based on my limited understanding of newLISP, leads to this:


(context 'foo)
(set 'a 10)

(context 'bar)
(set 'a 20)

(context MAIN)

(define (p c s)
   (println (eval (sym s c))))
   
(p 'foo 'a)
; 10

(p 'bar 'a)
; 20


But I suspect that you don't mean anything as simple as this...?! :-)

Jeff

#2
I'm talking about dynamically binding a function to a context for the period of execution, something perhaps along the lines of:


(set 'foo:a 10)
(set 'a 20)
(define (f) (println a))
(def-new 'f 'foo:f)
(foo:f)
(delete 'foo:f)
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code

Lutz

#3
QuoteI'm talking about dynamically binding a function to a context for the period of execution


no, all symbols occuring in your function are bound during code translation, not application.


(set 'x "hello")
"hello"
> (set 'x "main hello")
"main hello"
> (set 'ctx:x "ctx hello")
"ctx hello"
> (define (foo) (println x))
(lambda () (println x))
> (set 'ctx:foo foo)
(lambda () (println x))
> (ctx:foo)
main hello
"main hello"
> (foo)
main hello
"main hello"
>


Lutz

Jeff

#4
Does def-new and new bind new symbols in the target context, then?  What about a dynamically built lambda or a lambda templated from a macro?  Are their symbols late-bound in the context in which they were created?  Or are they bound in the context in which the forming function is defined?
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code

Lutz

#5
QuoteDoes def-new and new bind new symbols in the target context, then?


yes, they create new symbols in the target context


QuoteWhat about a dynamically built lambda or a lambda templated from a macro?


yes, you can build lambda expressions dynamically, just like you can create lists dynamically, but using existing symbols.


QuoteAre their symbols late-bound in the context in which they were created?


yes, for example in this case:




> (define (foo ctx value) (set 'ctx:data value))
(lambda (ctx value) (set 'ctx:data value))
>
> (context 'myctx) (context MAIN)
myctx
MAIN
> (foo myctx 123)
123
> myctx:data
123
>


When foo is called with 'myctx' as a context, 'myctx:data' does not yet exist, but gets created on demand. Before 'foo' was called the first time 'data' did not exist in any context. After the call it exists in 'myctx'. When calling 'foo' again with yet another context, a new 'data' in that other context could be created if it does not exist yet.



Any object system for newLISP should not try to build objects as namespaces but rather use lists and use the namespace only to store class methods. Read the new chapter "17. Object-Oriented Programming in newLISP" in the latest development release regarding all this. Any object system trying to imitate Scheme closures or JavaScript like function namespaces will not work well for newLISP because of the relative inefficiency of deleting symbols.



For newLISP the best way to do OO programming is, to use namespaces only for organizing methods into classes and build objects using lists, where the first list member points to the class, and the new ':' colon operator implements polymorphism (read chapter 17. ;-) ). Doing it this way objects can be anonymous, have very little overhead and can be memory managed by newLISP efficiently.



Lutz

Jeff

#6
I wasn't intending to use newLISP for OOP.  I was writing an event-based application and one thing that is extremely helpful in that is controlling the environment in which a callback is executed.  Javascript has a very neat model for this.  The ability to dynamically bind a function to the scope desired makes event-based programming efficient and elegant.  It's a shame newLISP can't do this.
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code

Lutz

#7
QuoteThe ability to dynamically bind a function to the scope desired makes event-based programming efficient and elegant.


Event-based programming is very much related to OO programming and message passing. Originally OO programming was developed for implementening event based programs (see the Simula programming language). In the new newLISP OOP model you could see objects as implementing different scopes receiving a message:


(:message object)

The : operator ensures the late binding of the message to the suiting scope and method of the object.



Lutz



ps: there is a second (older) method of binding functions to different scopes based on the dynamic scoping mechanism inside one namespace in newISP. You could pass your function as parameter to another lambda-object:


(define (report-message)
    (print x))

(define (object-a f (x "hello"))
    (f x))

(define (object-b f (x "world"))
    (f x))

(object-a report-message) ; outputs "hello"
(object-b report-message) ; outputs "world"


of course this works only if objects are in the same namespace. The OOP based method would not have this limitation.

Fanda

#8
I also tried:


(context 'foo)
(set 'x 5)
(define (ctx-eval-string)
(apply eval-string (args)))

(context 'bar)
(set 'x 10)
(define (ctx-eval-string)
(apply eval-string (args)))

(context MAIN)

(define (f) x)
(define (g a) (* a x))

(define (fn->ctx-fn _ctx _f)
(apply (sym 'ctx-eval-string _ctx) (list (string _f))))


Example:
> f
(lambda () x)
> g
(lambda (a) (* a x))
> (fn->ctx-fn 'foo f)
(lambda () foo:x)
> (fn->ctx-fn 'bar f)
(lambda () bar:x)
> (fn->ctx-fn 'foo g)
(lambda (foo:a) (* foo:a foo:x))
> (fn->ctx-fn 'bar g)
(lambda (bar:a) (* bar:a bar:x))
>
> ((fn->ctx-fn 'foo f))
5
> ((fn->ctx-fn 'foo g) 10)
50
> ((fn->ctx-fn 'bar f))
10
> ((fn->ctx-fn 'bar g) 10)
100
>
> (set 'myg (fn->ctx-fn 'foo g))
(lambda (foo:a) (* foo:a foo:x))
> (myg 10)
50


It works as long as 'ctx-eval-string' function is in every context you want to work with.



Fanda