[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.programming

TXR Lisp: OOP producer/consumer based on delimited continuations.

Kaz Kylheku

4/4/2016 7:53:00 PM

TXR (http://www.nong...) is a new language that runs on a variety
of operating systems, geared toward scripting in Unix-like environments.
It processes text streams using pattern matching paradigm, and has a
powerful, nimble, original Lisp dialect called TXR Lisp which is used by
embeddeding Lisp expressions in the TXR language, or completely
standalone.

In the following TXR Lisp program, a simple class inheritance from
"thread", defining "run" methods emulates co-routines on top of
delimited continuations.

The producer and consumer are both sitting in a while loop.

The producer reads lines from standard input, and feeds these stings
to the consumer which prints them and also counts them.

When the producer hits EOF, it terminates, and the main statement
in the program peeks into the consumer and prints the count.

(defstruct thread nil
suspended
cont
(:method resume (self)
[self.cont])
(:method give (self item)
[self.cont item])
(:method get (self)
(yield-from run nil))
(:method start (self)
(set self.cont (obtain self.(run)))
(unless self.suspended
self.(resume)))
(:postinit (self)
self.(start)))

(defstruct consumer thread
(count 0)
(:method run (self)
(whilet ((item self.(get)))
(prinl item)
(inc self.count))))

(defstruct producer thread
consumer
(:method run (self)
(whilet ((line (get-line)))
self.consumer.(give line))))

(let* ((con (new consumer))
(pro (new producer suspended t consumer con)))
pro.(resume)
(put-line `count = @{con.count}`))

These are not co-routines, but delimited continuations, implemented
over the native C stack. Continuations are first-class functions that
are composable; they are not thread-like contexts. The appearance
of co-routine-like execution occurs due to the repeated capture of
numerous continuations "under the hood", which is hidden by the
yield-from and obtain macros, whch provide a simplified programming
moidel.

Each time the "coroutine" yields, it captures a brand new continuation,
which is installed in place of its previous one. This captured
continuation stored in a hidden variable which is captured in the
lexical environment of the dispatch function that the thread class
stores in "self.cont". That "self.cont" function is not the continuation
itself, in spite of its name, but a lambda which dispatches the latest
continuation (and is itself never replaced).

A continuation is restarted by generating brand new stack frames on the
stack (by copying them from the saved continuation object), fixing up
some things in these frames, and then performing a nonlocal jump to the
topmost frame.

Interactive use:

$ txr
This is the TXR Lisp interactive listener of TXR 137.
Use the :quit command or type Ctrl-D on empty line to exit.
1> (obtain (for ((i 0)) ((< i 10)) ((inc i)) (yield i)))
#<interpreted fun: lambda (: resume-val)>
2> (call *1)
0
3> (call *1)
1
4> (call *1)
2
5> (call *1)
3
6> (call *1)
4
7> (call *1)
5
8> (call *1)
6
9> [*1] ;; equivalent to (call *1)
7
10> [*1]
8
11> [*1]
9
12> [*1]
nil
13> [*1]
nil

This resembles generators (deliberately) but is much more powerful.
The (yield i) does not have to be lexically enclosed in the (obtain ...)
but dynamically. The for loop could call arbitrary functions to any nesting
depth, any of which could invoke yield, and then resume the entire
activation chain properly.