Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.programming > #6429 > unrolled thread

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

Started byKaz Kylheku <545-066-4921@kylheku.com>
First post2016-04-04 19:52 +0000
Last post2016-05-24 08:36 +0100
Articles 3 — 3 participants

Back to article view | Back to comp.programming


Contents

  TXR Lisp: OOP producer/consumer based on delimited continuations. Kaz Kylheku <545-066-4921@kylheku.com> - 2016-04-04 19:52 +0000
    Re: TXR Lisp: OOP producer/consumer based on delimited continuations. luser droog <mijoryx@yahoo.com> - 2016-05-23 21:51 -0700
      Re: TXR Lisp: OOP producer/consumer based on delimited continuations. Richard Heathfield <rjh@cpax.org.uk> - 2016-05-24 08:36 +0100

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

FromKaz Kylheku <545-066-4921@kylheku.com>
Date2016-04-04 19:52 +0000
SubjectTXR Lisp: OOP producer/consumer based on delimited continuations.
Message-ID<20160404122540.999@kylheku.com>
TXR (http://www.nongnu.org/txr) 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.

[toc] | [next] | [standalone]


#6652

Fromluser droog <mijoryx@yahoo.com>
Date2016-05-23 21:51 -0700
Message-ID<3b0a1bdf-7ef7-43db-92aa-659490c8ecc1@googlegroups.com>
In reply to#6429
On Monday, April 4, 2016 at 2:52:40 PM UTC-5, Kaz Kylheku wrote

Woah.

-- 
got to figure out how he does that!

[toc] | [prev] | [next] | [standalone]


#6654

FromRichard Heathfield <rjh@cpax.org.uk>
Date2016-05-24 08:36 +0100
Message-ID<ni102g$ff3$2@dont-email.me>
In reply to#6652
On 24/05/16 05:51, luser droog wrote:
> On Monday, April 4, 2016 at 2:52:40 PM UTC-5, Kaz Kylheku wrote
>
> Woah.

Uh, yeah. Kaz takes some getting used to, doesn't he?

-- 
Richard Heathfield
Email: rjh at cpax dot org dot uk
"Usenet is a strange place" - dmr 29 July 1999
Sig line 4 vacant - apply within

[toc] | [prev] | [standalone]


Back to top | Article view | comp.programming


csiph-web