unofficial mirror of guile-user@gnu.org 
 help / color / mirror / Atom feed
* C++ declaration style programming?
@ 2004-01-20 22:17 Han-Wen Nienhuys
  2004-01-21  3:58 ` Viktor Pavlenko
                   ` (4 more replies)
  0 siblings, 5 replies; 14+ messages in thread
From: Han-Wen Nienhuys @ 2004-01-20 22:17 UTC (permalink / raw)



Hi there,

I just realized that one of the things that I dislike about Scheme is
that my habit of naming intermediate results (which I inherited from
C++) leads to deep nesting.

Usually in C++, I do

  int var1 = something ();
  int var2 = something (var1);  
  int var3 = something (var1, var2);  
    etc.

in Scheme, this translates quite well to (let* ) :

(let*
  ((var1 (something))
   (var2 (something var1))
   (var3 (something var1 var2)))
  .. )

However, it doesn't work so well when I mix commands with
declarations, eg.


  int var1 = something ();
  var1 += 2; 
  int var2 = something (var1);
  var2 += var1;
  int var3 = something (var1, var2);  
    etc.

I would like to have some macro, where I can write the Scheme analogon
like

 (begin-let*
  (def var1 (something))
  (set! var1 (+ var1 2))
  (def var2 (something var1))
  (set! var2 (+ var2 var1))
  (def var3 (something var1 var2))
  ...  )


This can presumably be done by writing a macro begin-let* that expands
the statement list in a suitably nested let*

However, I was wondering whether there exists a standard library
syntax mechanism that lets me write code in this fashion.

-- 

 Han-Wen Nienhuys   |   hanwen@xs4all.nl   |   http://www.xs4all.nl/~hanwen 



_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: C++ declaration style programming?
  2004-01-20 22:17 Han-Wen Nienhuys
@ 2004-01-21  3:58 ` Viktor Pavlenko
  2004-01-21 12:40   ` Han-Wen Nienhuys
  2004-01-21  4:57 ` Issac Trotts
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 14+ messages in thread
From: Viktor Pavlenko @ 2004-01-21  3:58 UTC (permalink / raw)
  Cc: guile-user

>>>>> "HN" == Han-Wen Nienhuys <hanwen@xs4all.nl> writes:

    HN> Usually in C++, I do

    HN>   int var1 = something ();
    HN>   int var2 = something (var1);  
    HN>   int var3 = something (var1, var2);  
    HN>     etc.

    HN> in Scheme, this translates quite well to (let* ) :

    HN> (let*
    HN>   ((var1 (something))
    HN>    (var2 (something var1))
    HN>    (var3 (something var1 var2)))
    HN>   .. )

    HN> However, it doesn't work so well when I mix commands with
    HN> declarations, eg.

    HN>   int var1 = something ();
    HN>   var1 += 2; 
    HN>   int var2 = something (var1);
    HN>   var2 += var1;
    HN>   int var3 = something (var1, var2);  
    HN>     etc.

I think the following still looks OK and cleaner than the C above:

(let* ((var1 (+ (something) 2))
       (var2 (+ (something var1) var1))
       (var3 (something var1 var2)))

If you need to change var3 at a later point, just give it another name:

  (let ((var3-2 (+ (something-else var3))))
    (...

instead of set!-ting it. The problem with set! in a lexically scoped
language is that you can change the binding of a variable defined at a
higher level by mistake:

guile> (define a 123)
guile> a
123
guile> (let ((a 111)) (set! a 444)) ;;works as expected
guile> a
123
guile> (let ((b 111)) (set! a 444)) ;;oops
guile> a
444

Lexical bindings with let and friends will never get you into this
trouble.

(Scheme gurus, please correct me if my reasoning is wrong)

    HN> I would like to have some macro, where I can write the Scheme
    HN> analogon like

    HN>  (begin-let*
    HN>   (def var1 (something))
    HN>   (set! var1 (+ var1 2))
    HN>   (def var2 (something var1))
    HN>   (set! var2 (+ var2 var1))
    HN>   (def var3 (something var1 var2))
    HN>   ...  )

Probably you can define that syntax, but the passage above doesn't
look like Scheme...

-- 
Viktor


_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: C++ declaration style programming?
  2004-01-20 22:17 Han-Wen Nienhuys
  2004-01-21  3:58 ` Viktor Pavlenko
@ 2004-01-21  4:57 ` Issac Trotts
  2004-01-21  5:00 ` Stephen Compall
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 14+ messages in thread
From: Issac Trotts @ 2004-01-21  4:57 UTC (permalink / raw)


On Tue, Jan 20, 2004 at 11:17:34PM +0100, Han-Wen Nienhuys wrote:
> 
> Hi there,
> 
> I just realized that one of the things that I dislike about Scheme is
> that my habit of naming intermediate results (which I inherited from
> C++) leads to deep nesting.
> 
> Usually in C++, I do
> 
>   int var1 = something ();
>   int var2 = something (var1);  
>   int var3 = something (var1, var2);  
>     etc.
> 
> in Scheme, this translates quite well to (let* ) :
> 
> (let*
>   ((var1 (something))
>    (var2 (something var1))
>    (var3 (something var1 var2)))
>   .. )
> 
> However, it doesn't work so well when I mix commands with
> declarations, eg.
> 
> 
>   int var1 = something ();
>   var1 += 2; 
>   int var2 = something (var1);
>   var2 += var1;
>   int var3 = something (var1, var2);  
>     etc.

Well, in this case you could do something more functional:

(define (something . x) 1)
(let*
  ((var1 (+ 2 (something)))
   (var2 (+ (something var1) var1))
   (var3 (something var1 var2)))
  (display `(,var1 ,var2 ,var3)))

> This can presumably be done by writing a macro begin-let* that expands
> the statement list in a suitably nested let*
> 
> However, I was wondering whether there exists a standard library
> syntax mechanism that lets me write code in this fashion.
> 
> -- 
> 
>  Han-Wen Nienhuys   |   hanwen@xs4all.nl   |   http://www.xs4all.nl/~hanwen 
> 
> 
> 
> _______________________________________________
> Guile-user mailing list
> Guile-user@gnu.org
> http://mail.gnu.org/mailman/listinfo/guile-user

-- 
Issac Trotts
http://redwood.ucdavis.edu/~issac



_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: C++ declaration style programming?
  2004-01-20 22:17 Han-Wen Nienhuys
  2004-01-21  3:58 ` Viktor Pavlenko
  2004-01-21  4:57 ` Issac Trotts
@ 2004-01-21  5:00 ` Stephen Compall
  2004-01-21 12:45   ` Han-Wen Nienhuys
  2004-01-21  5:03 ` Lynn Winebarger
  2004-01-21 21:46 ` Keith Wright
  4 siblings, 1 reply; 14+ messages in thread
From: Stephen Compall @ 2004-01-21  5:00 UTC (permalink / raw)
  Cc: guile-user

Han-Wen Nienhuys  <hanwen@xs4all.nl> writes:

>  (begin-let*
>   (def var1 (something))
>   (set! var1 (+ var1 2))
>   (def var2 (something var1))
>   (set! var2 (+ var2 var1))
>   (def var3 (something var1 var2))
>   ...  )
> 
> 
> This can presumably be done by writing a macro begin-let* that expands
> the statement list in a suitably nested let*
> 
> However, I was wondering whether there exists a standard library
> syntax mechanism that lets me write code in this fashion.

Viktor Pavlenko is correct in saying that this is just not what you do
in Scheme.  As Stroustrup (I believe) said, you cannot simply take
idioms from one language and use them in another.

That said, here is a macro that does the thing.  Note, however, that:

 1. it arbitrarly interprets forms whose car is 'def to be your define
    forms

 2. you can re-def forms and in so doing shadow their previous
    definitions (not that this makes any difference, as the scope
    continues to the end of begin-let*)

 3. because of 1., def forms buried deeper than the
    immediate level will not be found.  So (begin-let* (def hello
    "hello")) is ok, (begin-let* (begin (def hello "hello") (display
    hello) (newline))) is not ok.

 4. also because of 1., you can't (def def newline) and expect (def)
    as one of your forms to be the same as (newline).  (def) will be
    changed to (let (()) ...), which of course will throw
    syntax-error.

 5. This def doesn't include the function definition shortcut,
    i.e. "having the first argument be a pair means you're defining a
    function" isn't recognized.  Use lambda instead.  Not that you'd
    do that, being so against the Schemely way of doing things >:->

I suggest you document this macro before using it.

(define-macro (begin-let* . forms)
  (let ((newforms (list 'begin)))
    (let lp ((forms forms) (lastpair newforms))
      (cond
       ((not (pair? forms)) newforms)
       ((and (pair? (car forms)) (eq? 'def (caar forms)))
        (set-cdr! lastpair `((let (,(cdar forms)))))
        (lp (cdr forms) (cdadr lastpair)))
       (else
        (set-cdr! lastpair (list (car forms)))
        (lp (cdr forms) (cdr lastpair)))))))

--
Stephen Compall or s11 or sirian

Many receive advice, few profit by it.
		-- Publilius Syrus

64 Vauxhall Cross csystems passwd CDC Audiotel Skipjack tempest
Albright ARPA assassination Ft. Bragg satellite imagery clones ASIO
EuroFed


_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: C++ declaration style programming?
  2004-01-20 22:17 Han-Wen Nienhuys
                   ` (2 preceding siblings ...)
  2004-01-21  5:00 ` Stephen Compall
@ 2004-01-21  5:03 ` Lynn Winebarger
  2004-01-21 21:46 ` Keith Wright
  4 siblings, 0 replies; 14+ messages in thread
From: Lynn Winebarger @ 2004-01-21  5:03 UTC (permalink / raw)



Han-Wen Nienhuys wrote:

 >  (begin-let*
 >   (def var1 (something))
 >   (set! var1 (+ var1 2))
 >   (def var2 (something var1))
 >   (set! var2 (+ var2 var1))
 >   (def var3 (something var1 var2))
 >   ...  )
 >
 >
 > This can presumably be done by writing a macro begin-let* that expands
 > the statement list in a suitably nested let*
 >
 > However, I was wondering whether there exists a standard library
 > syntax mechanism that lets me write code in this fashion.
 >

    Untested...

(define-syntax begin-let*
    (syntax-rules ()
      ((_ args ...)
       (begin-let*-helper () () (args ...)))))

(define-syntax begin-let*-helper
    (syntax-rules (def)
      ((_ (var ...) (statement ...) ())
       (let ((var #f) ...) statement ...))
      ((_ vars (statement ...) ((def x y) . rest))
       (begin-let*-helper (x . vars) (statement ... (set! x y)) rest))
      ((_ vars (statement ...) (non-decl . rest))
       (begin-let*-helper vars (statement ... (set! x y)) rest))))


   You could be slightly more clever about reversing the statements _after_
accumulating them, rather than appending (and building the same list over
and over and over...).  But I'll leave that as an easy exercise.
    Odd to call it "declaration style", though.

Lynn




_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: C++ declaration style programming?
  2004-01-21  3:58 ` Viktor Pavlenko
@ 2004-01-21 12:40   ` Han-Wen Nienhuys
  0 siblings, 0 replies; 14+ messages in thread
From: Han-Wen Nienhuys @ 2004-01-21 12:40 UTC (permalink / raw)
  Cc: guile-user

vvp@rogers.com writes:
> 
> (let* ((var1 (+ (something) 2))
>        (var2 (+ (something var1) var1))
>        (var3 (something var1 var2)))
> 
> If you need to change var3 at a later point, just give it another name:
> 
>   (let ((var3-2 (+ (something-else var3))))
>     (...
> 
> instead of set!-ting it. The problem with set! in a lexically scoped
> language is that you can change the binding of a variable defined at a
> higher level by mistake:

I know about all this. The example was a little contrived. The things
I have in mind are statements which currently return SCM_UNSPECIFIED.

I think it looks stupid to do

  (let* ((a (something))
	 (unused (display a))
	 (b (something-else a))
	 ..
	 )

which introduces a variable unused, and puts emphasis on the
declaration, not on the main action of the statements.

-- 

 Han-Wen Nienhuys   |   hanwen@xs4all.nl   |   http://www.xs4all.nl/~hanwen 



_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: C++ declaration style programming?
  2004-01-21  5:00 ` Stephen Compall
@ 2004-01-21 12:45   ` Han-Wen Nienhuys
  0 siblings, 0 replies; 14+ messages in thread
From: Han-Wen Nienhuys @ 2004-01-21 12:45 UTC (permalink / raw)
  Cc: guile-user

s11@member.fsf.org writes:
> > This can presumably be done by writing a macro begin-let* that expands
> > the statement list in a suitably nested let*
> > 
> > However, I was wondering whether there exists a standard library
> > syntax mechanism that lets me write code in this fashion.
> 
> Viktor Pavlenko is correct in saying that this is just not what you do
> in Scheme.  As Stroustrup (I believe) said, you cannot simply take
> idioms from one language and use them in another.

I'm not a fan of Stroustrup or C++, but I need to get over some gripes
that I have with Scheme. This macro can help, and if the Schemely way
of doing things is correct, then I'll find out why other ways are
wrong once I start using them. :)

Thanks for the macro definition.

> I suggest you document this macro before using it.

will do. 

--
 Han-Wen Nienhuys   |   hanwen@xs4all.nl   |   http://www.xs4all.nl/~hanwen 



_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: C++ declaration style programming?
@ 2004-01-21 14:22 Viktor Pavlenko
  2004-01-21 14:26 ` Han-Wen Nienhuys
  0 siblings, 1 reply; 14+ messages in thread
From: Viktor Pavlenko @ 2004-01-21 14:22 UTC (permalink / raw)
  Cc: guile-user

> From: Han-Wen Nienhuys  <hanwen@xs4all.nl>
> Date: 2004/01/21 Wed AM 07:40:42 EST
> 
> I think it looks stupid to do
> 
>   (let* ((a (something))
> 	 (unused (display a))
> 	 (b (something-else a))
> 	 ..
> 	 )
> 

Then what about this:

(let ((a (something)))
  (display a)
  (let ((b (something-else a)))
    ..

or

(let* ((a (something))
       (b (something-else a)))
  (display a)
  ..

Viktor





_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: C++ declaration style programming?
  2004-01-21 14:22 Viktor Pavlenko
@ 2004-01-21 14:26 ` Han-Wen Nienhuys
  0 siblings, 0 replies; 14+ messages in thread
From: Han-Wen Nienhuys @ 2004-01-21 14:26 UTC (permalink / raw)
  Cc: guile-user

vvp@rogers.com writes:
> Then what about this:
> 
> (let ((a (something)))
>   (display a)
>   (let ((b (something-else a)))
>     ..
> 
> or
> 
> (let* ((a (something))
>        (b (something-else a)))
>   (display a)
>   ..

Your 2nd is not always possible due to side effects, your 1st was my
first gripe: it introduces deep nesting if you do this 10 times.

 

-- 

 Han-Wen Nienhuys   |   hanwen@xs4all.nl   |   http://www.xs4all.nl/~hanwen 



_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: C++ declaration style programming?
@ 2004-01-21 15:41 Viktor Pavlenko
  2004-01-21 16:06 ` Paul Jarc
  2004-01-22  2:24 ` Thien-Thi Nguyen
  0 siblings, 2 replies; 14+ messages in thread
From: Viktor Pavlenko @ 2004-01-21 15:41 UTC (permalink / raw)
  Cc: guile-user

> From: Han-Wen Nienhuys  <hanwen@xs4all.nl>
> Date: 2004/01/21 Wed AM 09:26:12 EST

> > 
> > (let* ((a (something))
> >        (b (something-else a)))
> >   (display a)
> >   ..
> 
> Your 2nd is not always possible due to side effects [...]

let* is a binding construct by definition and it is an error to
bind the same identifier twice (r5rs). Using side effects to re-bind
a variable within the binding expression of let* sounds like cheating :)

Viktor





_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: C++ declaration style programming?
  2004-01-21 15:41 C++ declaration style programming? Viktor Pavlenko
@ 2004-01-21 16:06 ` Paul Jarc
  2004-01-22  2:24 ` Thien-Thi Nguyen
  1 sibling, 0 replies; 14+ messages in thread
From: Paul Jarc @ 2004-01-21 16:06 UTC (permalink / raw)
  Cc: guile-user, hanwen

Viktor Pavlenko <vvp@rogers.com> wrote:
> let* is a binding construct by definition and it is an error to
> bind the same identifier twice (r5rs).

Guile complains about duplicate bindings in let or letrec, but not
let*; the second binding simply shadows the first.  I believe that's
what R5RS specifies.  (So it is possible to accidentally shadow an
existing binding, which may cause trouble in subsequent code similar
to set!.)

> Using side effects to re-bind a variable within the binding
> expression of let* sounds like cheating :)

There are other forms of side effects, like I/O.


paul


_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: C++ declaration style programming?
  2004-01-20 22:17 Han-Wen Nienhuys
                   ` (3 preceding siblings ...)
  2004-01-21  5:03 ` Lynn Winebarger
@ 2004-01-21 21:46 ` Keith Wright
  4 siblings, 0 replies; 14+ messages in thread
From: Keith Wright @ 2004-01-21 21:46 UTC (permalink / raw)


> From: Han-Wen Nienhuys  <hanwen@xs4all.nl>
> 
> However, it doesn't work so well when I mix commands with
> declarations, eg.
> 
> 
>   int var1 = something ();
>   var1 += 2; 
>   int var2 = something (var1);
>   var2 += var1;
>   int var3 = something (var1, var2);  
>     etc.
> 

I realize this is just an example of syntax, several people
have pointed out that computing a value of 'var1' only to
change it has some lameness to it, but if you need to do that...

(let* ((var1 (let ((var1-ini (something)))
               (+ 2 var1-ini)))
       (var2 (let ((var2-ini (something var1)))
               (+ var2-ini var1)))
       (var3 (something var1 var2)) )
  etc)

You can use the first guess value as many times as you
need to compute the final value, but then it goes out
of scope and doesn't clutter all that follows.

If the imperative commands are part of computing the following
variable then...

(let*  ((var1 (something))
        (var2 (begin (set! var1 (+ 2 var1))
                     (something var1) ))
        (var3 (begin (set! var2 (+ var2 var1))
                     (something var1 var2) )))
  etc)

If the imperative stuff is not related to either the preceding
or following declaration, then maybe it should be moved out of
the middle.

Just a thought, hope this helps,
  do what you will is the whole of the law.
-- 
     -- Keith Wright  <kwright@free-comp-shop.com>

Programmer in Chief, Free Computer Shop <http://www.free-comp-shop.com>
         ---  Food, Shelter, Source code.  ---


_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: C++ declaration style programming?
  2004-01-21 15:41 C++ declaration style programming? Viktor Pavlenko
  2004-01-21 16:06 ` Paul Jarc
@ 2004-01-22  2:24 ` Thien-Thi Nguyen
  2004-01-22  4:28   ` Viktor Pavlenko
  1 sibling, 1 reply; 14+ messages in thread
From: Thien-Thi Nguyen @ 2004-01-22  2:24 UTC (permalink / raw)


   From: Viktor Pavlenko <vvp@rogers.com>
   Date: Wed, 21 Jan 2004 10:41:09 -0500

   Using side effects to re-bind a variable within the binding
   expression of let* sounds like cheating :)

well, here's another way to "cheat", i suppose:

(defmacro let-false (bindings . body)
  `(let ,(map (lambda (b) (list b #f)) bindings) ,@body))

usage is something like:

(let-false (a b c) 1) => 1
(let-false (a b c) b) => #f
(let-false (a b c)
  (set! a 2)
  (set! b (+ a (* a a)))
  (set! a (+ b (* b b)))
  (list a (not c))) => the answer to life the universe and everything?

(let-false (a b c) d)
=| ERROR: Unbound variable: d
   ABORT: (unbound-variable)

if "false" is too much of a downer, you can munge to taste of course.
"let-pessimistic", "let-oh-why-bother-i-have-a-brain-the-size-of-a-planet".

thi


_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: C++ declaration style programming?
  2004-01-22  2:24 ` Thien-Thi Nguyen
@ 2004-01-22  4:28   ` Viktor Pavlenko
  0 siblings, 0 replies; 14+ messages in thread
From: Viktor Pavlenko @ 2004-01-22  4:28 UTC (permalink / raw)
  Cc: guile-user

>>>>> "TTN" == Thien-Thi Nguyen <ttn@surf.glug.org> writes:

    TTN> (defmacro let-false (bindings . body)
    TTN>   `(let ,(map (lambda (b) (list b #f)) bindings) ,@body))

    TTN> (let-false (a b c)
    TTN>   (set! a 2)
    TTN>   (set! b (+ a (* a a)))
    TTN>   (set! a (+ b (* b b)))
    TTN>   (list a (not c))) => the answer to life the universe and everything?

If we're in a contest of getting the answer to all questions from 2,
count me in:

(let ((x 2))
  (string->number
   ((lambda (x) (string-append x x x)) (number->string x x)) x))

cheers

-- 
Viktor


_______________________________________________
Guile-user mailing list
Guile-user@gnu.org
http://mail.gnu.org/mailman/listinfo/guile-user


^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2004-01-22  4:28 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-01-21 15:41 C++ declaration style programming? Viktor Pavlenko
2004-01-21 16:06 ` Paul Jarc
2004-01-22  2:24 ` Thien-Thi Nguyen
2004-01-22  4:28   ` Viktor Pavlenko
  -- strict thread matches above, loose matches on Subject: below --
2004-01-21 14:22 Viktor Pavlenko
2004-01-21 14:26 ` Han-Wen Nienhuys
2004-01-20 22:17 Han-Wen Nienhuys
2004-01-21  3:58 ` Viktor Pavlenko
2004-01-21 12:40   ` Han-Wen Nienhuys
2004-01-21  4:57 ` Issac Trotts
2004-01-21  5:00 ` Stephen Compall
2004-01-21 12:45   ` Han-Wen Nienhuys
2004-01-21  5:03 ` Lynn Winebarger
2004-01-21 21:46 ` Keith Wright

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).