Anonymous contexts (again)

Started by Jeff, July 29, 2008, 12:12:56 PM

Previous topic - Next topic

Jeff

Lutz,



May we have anonymous contexts back?  I know you don't like them.  I know you think they are inefficient.  But they are handy.  They allow complex programs to be written concisely and expressively.  They open up a variety of techniques to us.  I want to be able to make function factories that return new functors with their own static environments.  



It doesn't hurt the language to have this available; it is ultimately up to the programmer to write code that is as efficient as is reasonable.  The language should not constrain them unnecessarily.
Jeff

=====

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



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

Kazimir Majorinc

#1
I tend to agree with Jeff, if efficiency is only issue with anonymous contexts.
http://kazimirmajorinc.com/\">WWW site; http://kazimirmajorinc.blogspot.com\">blog.

Lutz

#2
For a function making functions with state, a function 'def-static' could be defined like this:



(define-macro (def-static)
    (let (temp (append (lambda) (list (1 (args 0)) (args 1))))
        (def-new 'temp (sym (args 0 0) (args 0 0)))))

; use the macro

(def-static (acc x)
    (if sum
        (inc 'sum x)
        (set 'sum x)))

; or the same in v.9.4.4 and higher

(def-static (acc x)  (inc 'sum x))

; use it:
> (acc 5)
5
> (acc 5)
10
> (acc 5)
15
> acc:sum
15
>


This macro basically defines a function as a default function with its own context. Any free variables (i.e. sum) mentioned, then end up in that context's static namespace.



The macro is hygienic as it is written using (args).



ps: note that 'def-new' works replicating symbol from the MAIN name space into the new created context including a copy of the contents of a symbol. This means that you should take care of properly initializing free variables in the new function.



ps: Totally anonymous contexts are not possible without major architectural changes. But for transient (quasi anonymous :-) ) contexts you can use this (posted earlier):


(define (foo x)
    (let (ctx 999)
        (println "ctx:" ctx)
        (context 'ctx)
        (set 'ctx:x 123)
        (println "current context:" (context))
        (println "symbols in ctx: " (symbols ctx))
        (println "ctx:x: " ctx:x)
        (println "x:" x)
        (delete 'ctx)
))

> (foo 10)
ctx:999
current context:ctx
symbols in ctx: (x)
ctx:x: 123
x:10
true
>

Jeff

#3
The problem with def-static is that you don't always know ahead of time how many instances you will need (although it's a technique I had not thought of).



I could use my gen-sym function (in utils.lsp on artful code) to create a new symbol, but in a long-running application that isn't a good idea.



I think this is a direction that you ought to take newLISP in, simply because of the power it brings to the language.
Jeff

=====

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



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

Lutz

#4
Before version 7.2.8 newLISP had lambda expressions which allowed for static variables. But they where eliminated in favor of namespaces, received by the community with much more enthusiasm, because they can be used for many different things, besides supplying a lambda expression with static memory.



One of the reasons I don't like closures and anonymous namespaces is the difficulty to serialize these data with 'source' or 'save'; you have to define new functions to go with it, written out in the serialized form to capture the current state. In newLISP everything can be serialized to easy recognizable and readable ascii/utf-8 source code without these complications.



But the main reason is, that from an application point of view there is nothing which can't be done using namespaces, and in my experience easier to understand and less abstract than a function closure.

Kazimir Majorinc

#5
To some extent one can use following technique:


Quote> (set 'kiki (lambda()1(nth 1 kiki)))

(lambda () 1 (nth 1 kiki))

> (kiki)

1

> (set-nth (kiki 1) 100)

(lambda () 100 (nth 1 kiki))

> (kiki)

100

>


I used that in the post http://kazimirmajorinc.blogspot.com/2008/05/embeding-blocks-into-functions.html">Embedding Blocks into Functions, although in quite primitive way.



If functions have some system variable, say, $me or $self such changes could be done even from the function itself. I don't know if implementation permits $me - or if that already exists.
http://kazimirmajorinc.com/\">WWW site; http://kazimirmajorinc.blogspot.com\">blog.

Lutz

#6
a technique based on the same principle is, to change parameter defaults:


(define (foo (sum 1)) (nth-set (foo 0 -1 -1) (inc 'sum)))

> (foo)
1
> (foo)
2
> (foo)
3
> (foo)
4
> (foo 10)
5
> (foo)
11
> (foo)
12
>


This gives you a variable to use at the same time.



But I prefer the following approach using namespaces:


(define (foo:foo x) (if x (set 'foo:sum x) (inc 'foo:sum))

> (foo)
1
> (foo)
2
> (foo 10)
10
> (foo)
11
> foo:sum => 11


Its better because you don't have to write extra accessor functions to access the sum. It also has more of an OO feeling to it, because foo:sum carries the 'foo' qualifier in front. Last not least (again ;-) ) its easier to understand than a closure.

Jeff

#7
I like the idea of "easy" closures using dynamically scoped namespaces specific to a function.  That's why I want to be able to replicate functors anonymously.  So I don't have to resort to hacks like setting parameter defaults.
Jeff

=====

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



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