Controlling function scope

For the Compleat Fan
Locked
Jeff
Posts: 604
Joined: Sat Apr 07, 2007 2:23 pm
Location: Ohio
Contact:

Controlling function scope

Post by 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:

Code: Select all

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

Code: Select all

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

Artful code

cormullion
Posts: 2038
Joined: Tue Nov 29, 2005 8:28 pm
Location: latiitude 50N longitude 3W
Contact:

Post by cormullion »

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

Code: Select all

(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
Posts: 604
Joined: Sat Apr 07, 2007 2:23 pm
Location: Ohio
Contact:

Post by Jeff »

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

Code: Select all

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

Artful code

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

Post by Lutz »

I'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.

Code: Select all

(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
Posts: 604
Joined: Sat Apr 07, 2007 2:23 pm
Location: Ohio
Contact:

Post by Jeff »

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

Artful code

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

Post by Lutz »

Does def-new and new bind new symbols in the target context, then?
yes, they create new symbols in the target context
What 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.
Are their symbols late-bound in the context in which they were created?
yes, for example in this case:

Code: Select all

> (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
Posts: 604
Joined: Sat Apr 07, 2007 2:23 pm
Location: Ohio
Contact:

Post by Jeff »

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

Artful code

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

Post by Lutz »

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

Code: Select all

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

Code: Select all

(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
Posts: 253
Joined: Tue Aug 02, 2005 6:40 am
Contact:

Post by Fanda »

I also tried:

Code: Select all

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

Code: Select all

> 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

Locked