Securing a future for anonymous functions in Python

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Nick Coghlan

    #1

    Securing a future for anonymous functions in Python

    GvR has commented that he want to get rid of the lambda keyword for Python 3.0.
    Getting rid of lambda seems like a worthy goal, but I'd prefer to see it dropped
    in favour of a different syntax, rather than completely losing the ability to
    have anonymous functions.

    Anyway, I'm looking for feedback on a def-based syntax that came up in a recent
    c.l.p discussion:


    Cheers,
    Nick.

    --
    Nick Coghlan | ncoghlan@email. com | Brisbane, Australia
    ---------------------------------------------------------------

  • Paul Rubin

    #2
    Re: Securing a future for anonymous functions in Python

    Nick Coghlan <ncoghlan@iinet .net.au> writes:[color=blue]
    > Anyway, I'm looking for feedback on a def-based syntax that came up in
    > a recent c.l.p discussion:[/color]

    Looks like just an even more contorted version of lambda. It doesn't
    fix lambda's main deficiency which is inability to have several
    statements in the anonymous function.

    Comment

    • Nick Coghlan

      #3
      Re: Securing a future for anonymous functions in Python

      Paul Rubin wrote:[color=blue]
      > Nick Coghlan <ncoghlan@iinet .net.au> writes:
      >[color=green]
      >>Anyway, I'm looking for feedback on a def-based syntax that came up in
      >>a recent c.l.p discussion:[/color]
      >
      >
      > Looks like just an even more contorted version of lambda. It doesn't
      > fix lambda's main deficiency which is inability to have several
      > statements in the anonymous function.[/color]

      Do you consider generator expressions or list comprehensions deficient because
      they don't allow several statements in the body of the for loop?

      Cheers,
      Nick.

      --
      Nick Coghlan | ncoghlan@email. com | Brisbane, Australia
      ---------------------------------------------------------------

      Comment

      • John Roth

        #4
        Re: Securing a future for anonymous functions in Python


        "Nick Coghlan" <ncoghlan@iinet .net.au> wrote in message
        news:mailman.85 97.1104413330.5 135.python-list@python.org ...[color=blue]
        > GvR has commented that he want to get rid of the lambda keyword for Python
        > 3.0. Getting rid of lambda seems like a worthy goal, but I'd prefer to see
        > it dropped in favour of a different syntax, rather than completely losing
        > the ability to have anonymous functions.
        >
        > Anyway, I'm looking for feedback on a def-based syntax that came up in a
        > recent c.l.p discussion:
        > http://boredomandlaziness.skystorm.n...in-python.html
        >
        > Cheers,
        > Nick.[/color]

        I think it's rather baroque, and I agree with Paul Ruben
        that it needs multiple statement capability.

        The syntax I prefer (and I don't know if it's actually been
        suggested before) is to use braces, that is { and }.

        In other words, an anonymous function looks like:
        {p1, p2, p3 |
        stmt1
        stmt2
        }

        There are two reasons for using braces. One is
        that it's the common syntax for blocks in a large number
        of languages. The other is that it should be relatively
        easy to disambiguate from dictionary literals, which are
        the only other current use of braces.

        The parameter list is optional, as is the bar ending
        the list. The reason for the bar instead of a colon
        is to help the parser in the case of a single parameter,
        which would look like the beginning of a dictionary
        literal. If the parser doesn't need the help, then a colon
        would be more consistent, hence better.

        A second issue is indentation. I'd set the indentation
        boundary wherever the _second_ line of the construct
        starts, as long as it's to the right of the prior
        indentation boundary. The unfortunate part of this, and one of the
        major stumbling blocks, is that it might take some
        significant reconceptualizi ng of the lexer and parser,
        which currently isn't set up to shift from expression
        back to statement mode and then return to expression
        mode.

        John Roth
        [color=blue]
        > --
        > Nick Coghlan | ncoghlan@email. com | Brisbane, Australia
        > ---------------------------------------------------------------
        > http://boredomandlaziness.skystorm.net[/color]

        Comment

        • Ian Bicking

          #5
          Re: Securing a future for anonymous functions in Python

          John Roth wrote:[color=blue]
          > The syntax I prefer (and I don't know if it's actually been
          > suggested before) is to use braces, that is { and }.
          >
          > In other words, an anonymous function looks like:
          > {p1, p2, p3 |
          > stmt1
          > stmt2
          > }[/color]

          What's the advantage of something like that over the non-anonymous
          equivalent:

          def some_func(p1, p2, p3):
          stmt1
          stmt2

          I appreciate some of the motivation, but merely avoiding giving
          something a name doesn't seem like a laudible goal.

          The one motivation I can see for function expressions is
          callback-oriented programming, like:

          get_web_page(ur l,
          when_retrieved= {page |
          give_page_to_ot her_object(mung e_page(page))})

          The problem with the normal function in this case is the order of
          statements is reversed:

          def when_retrieved_ callback(page):
          give_page_to_ot her_object(mung e_page(page))
          get_web_page(ur l, when_retrieved= when_retrieved_ callback)

          Oh, and you have to type the name twice, which is annoying. For most
          other functional programming constructs, list (and not generator)
          comprehensions work well enough, and the overhead of naming functions
          just isn't that big a deal.

          I think this specific use case -- defining callbacks -- should be
          addressed, rather than proposing a solution to something that isn't
          necessary. Which is to say, no one *needs* anonymous functions; people
          may need things which anonymous functions provide, but maybe there's
          other ways to provide the same thing. Decorator abuse, for instance ;)

          def get_web_page_de corator(url):
          def decorator(func) :
          return get_web_page(ur l, when_retrieved= func)
          return decorator

          @get_web_page_d ecorator(url)
          def when_retrieved( page):
          give_page_to_ot her_object(mung e_page(page))

          Or even (given a partial function as defined in some PEP, the number of
          which I don't remember):

          @partial(get_we b_page, url)
          def when_retrieved( page):
          give_page_to_ot her_object(mung e_page(page))

          It's okay not to like this proposal, I don't think I'm really serious.
          But I do think there's other ways to approach this. Function
          expressions could get really out of hand, IMHO, and could easily lead to
          twenty-line "expression s". That's aesthetically incompatible with
          Python source, IMHO.

          --
          Ian Bicking / ianb@colorstudy .com / http://blog.ianbicking.org

          Comment

          • Roy Smith

            #6
            Re: Securing a future for anonymous functions in Python

            Ian Bicking <ianb@colorstud y.com> wrote:[color=blue]
            > I think this specific use case -- defining callbacks -- should be
            > addressed, rather than proposing a solution to something that isn't
            > necessary. Which is to say, no one *needs* anonymous functions; people
            > may need things which anonymous functions provide, but maybe there's
            > other ways to provide the same thing. Decorator abuse, for instance ;)[/color]

            I'm not a big functional programming fan, so it should not come as a
            surprise that I don't often use lambda. The one place I do use it is in
            unit tests, where assertRaises() requires a callable. If what you're
            testing is an expression, you need to wrap it in a lambda.

            I suppose you could call this a special case of a callback.

            Comment

            • Skip Montanaro

              #7
              Re: Securing a future for anonymous functions in Python


              John> In other words, an anonymous function looks like:
              John> {p1, p2, p3 |
              John> stmt1
              John> stmt2
              John> }

              John> There are two reasons for using braces. One is that it's the
              John> common syntax for blocks in a large number of languages.

              Yeah, but it's not how blocks are spelled in Python. As Nick pointed out on
              his blog, allowing statements within expressions risks making code more
              difficult to read and understand.

              People keep trying to make Python something it is not. It is not
              fundamentally an expression-only language like Lisp, nor is it an
              expression-equals-statement language like C. There are good reasons why
              Guido chose the relationship between simple statements, compound statements
              and expressions that he did, readability and error avoidance being key.

              Skip

              Comment

              • Carl Banks

                #8
                Re: Securing a future for anonymous functions in Python

                Nick Coghlan wrote:[color=blue]
                > GvR has commented that he want to get rid of the lambda keyword for[/color]
                Python 3.0.[color=blue]
                > Getting rid of lambda seems like a worthy goal, but I'd prefer to see[/color]
                it dropped[color=blue]
                > in favour of a different syntax, rather than completely losing the[/color]
                ability to[color=blue]
                > have anonymous functions.[/color]

                I shall either coin or reuse a new term here: "premature optimization
                of the Python language."

                (Please note that the word premature is not intended to be an accurate
                description, but irony by analogy, so spare me any semantic
                nitpicking.)

                In much the same way that programmers often spend a lot of time
                optimizing parts of their program that will yield very minor dividends,
                while they could have spent that time working on other things that will
                pay off a lot, many of the wannabe language designers here are spending
                a lot of time on aspects of the language for which any improvement
                would only pay small dividends.

                I think the worry about anonymous functions is one of the most
                widespread cases of "premature optimization of the Python Language."
                One could argue about the various benefits of particular choices, maybe
                even make a convincing case that one is best in accord with the design
                goals of Python; but in the end, the divends are small compared to
                improving other aspects of the language.


                --
                CARL BANKS

                Comment

                • David Bolen

                  #9
                  Re: Securing a future for anonymous functions in Python

                  Ian Bicking <ianb@colorstud y.com> writes:
                  [color=blue]
                  > The one motivation I can see for function expressions is
                  > callback-oriented programming, like:
                  >
                  > get_web_page(ur l,
                  > when_retrieved= {page |
                  > give_page_to_ot her_object(mung e_page(page))})[/color]

                  This is my primary use case for lambda's nowadays as well - typically
                  just to provide a way to convert the input to a callback into a call
                  to some other routine. I do a lot of Twisted stuff, whose deferred
                  objects make heavy use of single parameter callbacks, and often you
                  just want to call the next method in sequence, with some minor change
                  (or to ignore) the last result.

                  So for example, an asynchronous sequence of operations might be like:

                  d = some_deferred_f unction()
                  d.addCallback(l ambda x: next_function() )
                  d.addCallback(l ambda blah: third_function( otherargs, blah))
                  d.addCallback(l ambda x: last_function() )

                  which to me is more readable (in terms of seeing the sequence of
                  operations being performed in their proper order), then something like:

                  def cb_next(x):
                  return next_function()
                  def cb_third(blah, otherargs):
                  return third_function( otherargs, blah)
                  def cb_last(x):
                  return last_function()

                  d = some_deferred_f unction()
                  d.addCallback(c b_next)
                  d.addCallback(c b_third, otherargs)
                  d.addCallback(c b_next)

                  which has an extra layer of naming (the callback functions), and
                  requires more effort to follow the flow of what is really just a simple
                  sequence of three functions being called.
                  [color=blue]
                  > I think this specific use case -- defining callbacks -- should be
                  > addressed, rather than proposing a solution to something that isn't
                  > necessary. (...)[/color]

                  I'd be interested in this approach too, especially if it made it simpler
                  to handle simple manipulation of callback arguments (e.g., since I often
                  ignore a successful prior result in a callback in order to just move on
                  to the next function in sequence).

                  -- David

                  Comment

                  • Bengt Richter

                    #10
                    Re: Securing a future for anonymous functions in Python

                    On Thu, 30 Dec 2004 23:28:46 +1000, Nick Coghlan <ncoghlan@iinet .net.au> wrote:
                    [color=blue]
                    >GvR has commented that he want to get rid of the lambda keyword for Python 3.0.
                    >Getting rid of lambda seems like a worthy goal, but I'd prefer to see it dropped
                    >in favour of a different syntax, rather than completely losing the ability to
                    >have anonymous functions.
                    >
                    >Anyway, I'm looking for feedback on a def-based syntax that came up in a recent
                    >c.l.p discussion:
                    >http://boredomandlaziness.skystorm.n...in-python.html
                    >[/color]
                    Nit: You didn't try the code you posted ;-)
                    [color=blue][color=green][color=darkred]
                    >>> funcs = [(lambda x: x + i) for i in range(10)]
                    >>>
                    >>> def incrementors():[/color][/color][/color]
                    ... for i in range(10):
                    ... def incrementor(x):
                    ... return x + i
                    ... yield incrementor
                    ...[color=blue][color=green][color=darkred]
                    >>> #funcs = list(incremento rs)[/color][/color][/color]
                    ... funcs2 = list(incremento rs())[color=blue][color=green][color=darkred]
                    >>>
                    >>> for f in funcs: print f(0),[/color][/color][/color]
                    ...
                    9 9 9 9 9 9 9 9 9 9[color=blue][color=green][color=darkred]
                    >>> for f in funcs2: print f(0),[/color][/color][/color]
                    ...
                    9 9 9 9 9 9 9 9 9 9

                    This is an easy trap to fall into, so if the new lambda-substitute could
                    provide a prettier current-closure-variable-value capture than passing a dummy default
                    value or nesting another def and passing the value in, to provide a private closure for each,
                    that might be something to think about.

                    IMO an anonymous def that exactly duplicates ordinary def except for leaving out
                    the function name and having a local indentation context would maximize flexibility
                    and also re-use of compiler code. People could abuse it, but that's already true
                    of many Python features.

                    From your web page:
                    ----
                    def either(conditio n, true_case, false_case):
                    if condition:
                    return true_case()
                    else:
                    return false_case()

                    print either(A == B, (def "A equals B"), (def "A does not equal B"))
                    either(thefile, (def thefile.close() ), (def 0))
                    ----

                    I'd rather see (:something) than (def something) for this special case,
                    but the full-fledged anonymous def would spell it thus:

                    print either(A == B, (def():return "A equals B"), (def(): return "A does not equal B"))
                    either(thefile, (def(): return thefile.close() ), (def(): return 0))

                    BTW,

                    funcs = [(lambda x: x + i) for i in range(10)]

                    might be spelled

                    funcs = [(def(x, i=i): return x + i) for i in range(10)]

                    or optionally

                    funcs = [(
                    def(x, i=i):
                    return x + i
                    ) for i in range(10)]

                    or

                    funcs = [(def(x, i=i):
                    return x + i) for i in range(10)]
                    or
                    funcs = [(def(x, i=i):
                    return x + i)
                    for i in range(10)]
                    or
                    funcs = [def(x, i=i):
                    return x + i
                    for i in range(10)]
                    or
                    funcs = [
                    def(x, i=i):
                    return x + i
                    for i in range(10)]

                    and so on. (the def defines the indentation base if the suite is indented, and the closing ')'
                    terminates the anonymous def explicitly, or a dedent to the level of the def or less can do it,
                    as in the last two examples).

                    This one

                    (def f(a) + g(b) - h(c) from (a, b, c))

                    would be spelled (if I undestand your example)

                    (def(a, b, c): return f(a)+g(b)+h(c))

                    which seems to me familiar and easy to understand.

                    BTW, there are old threads where this and other formats were discussed. I am still
                    partial to the full anonymous def with nesting indentation rules. Syntactic sugar
                    could be provided for useful abbreviations (that could possibly be expanded by the
                    tokenizer -- re which possibilities I haven't seen any discussion than my own
                    recent post, BTW), but I'd like the full capability.

                    Regards,
                    Bengt Richter

                    Comment

                    • John Roth

                      #11
                      Re: Securing a future for anonymous functions in Python


                      "Ian Bicking" <ianb@colorstud y.com> wrote in message
                      news:mailman.86 20.1104433674.5 135.python-list@python.org ...[color=blue]
                      > John Roth wrote:[color=green]
                      >> The syntax I prefer (and I don't know if it's actually been
                      >> suggested before) is to use braces, that is { and }.
                      >>
                      >> In other words, an anonymous function looks like:
                      >> {p1, p2, p3 |
                      >> stmt1
                      >> stmt2
                      >> }[/color]
                      >
                      > What's the advantage of something like that over the non-anonymous
                      > equivalent:
                      >
                      > def some_func(p1, p2, p3):
                      > stmt1
                      > stmt2
                      >
                      > I appreciate some of the motivation, but merely avoiding giving something
                      > a name doesn't seem like a laudible goal.[/color]

                      Actually, it is a laudable goal. It's always easier to understand
                      something when it's right in front of your face than if it's
                      off somewhere else.

                      This, of course, trades off with two other forces: avoiding
                      repetition and making the whole thing small enough to
                      understand.

                      So the niche is small, single use functions. The problem
                      with lambdas is that they're restricted to one expression,
                      which is too small.

                      Languages that are designed with anonymous functions
                      in mind use them very heavily. Smalltalk is the standard
                      example, and it's also one of the major (possibly the
                      only) attraction Ruby has over Python.

                      Python isn't designed that way, which restricts their
                      utility to a much smaller niche than otherwise.
                      [color=blue]
                      > The one motivation I can see for function expressions is callback-oriented
                      > programming ...[/color]

                      Well, that's true, but that's a very global statement:
                      when you pass a function into another routine, it's
                      essentially a callback.

                      ....
                      [color=blue]
                      > Function expressions could get really out of hand, IMHO, and could easily
                      > lead to twenty-line "expression s". That's aesthetically incompatible with
                      > Python source, IMHO.[/color]

                      Anything can get out of hand; there's no way of legislating
                      good style without restricting the language so much that it
                      becomes unusable for anything really interesting. Even then
                      it doesn't work: see COBOL as a really good example of
                      good intentions gone seriously wrong.

                      Have you ever programmed in a language that does use
                      anonymous functions extensively like Smalltalk?

                      John Roth[color=blue]
                      >
                      > --
                      > Ian Bicking / ianb@colorstudy .com / http://blog.ianbicking.org[/color]

                      Comment

                      • Jeff Shannon

                        #12
                        Re: Securing a future for anonymous functions in Python

                        David Bolen wrote:
                        [color=blue][color=green]
                        >>I think this specific use case -- defining callbacks -- should be
                        >>addressed, rather than proposing a solution to something that isn't
                        >>necessary. (...)
                        >>
                        >>[/color]
                        >
                        >I'd be interested in this approach too, especially if it made it simpler
                        >to handle simple manipulation of callback arguments (e.g., since I often
                        >ignore a successful prior result in a callback in order to just move on
                        >to the next function in sequence).
                        >
                        >[/color]

                        It seems to me that what most people *actually* want, when asking for
                        lambdas, is a quicker and more convenient way to get closures. (At
                        least, that's what the vast majority of examples of lambda use seem to
                        be for.) Perhaps one could get a bit more traction by looking for
                        improved closure syntax instead of focusing on the anonymous function
                        aspect.

                        All of the suggestions for anonymous multiline functions (with embedded
                        indentation) seem to me to miss what seems to me to be the only
                        significant benefit of lambda -- its ability to be used in-line without
                        creating a huge ugly tangle. (I'd argue that lambdas create a *small*
                        ugly tangle, but apparently that's just me. ;) ) Lambdas do have some
                        value in defining callbacks, but that value derives almost exclusively
                        from the fact that they are in-line (rather than a few lines above).
                        Mimicking function-def indentation inside of another function's arglist
                        strikes me as an abomination just waiting to happen; in comparison, the
                        need to type a name twice seems trivial.

                        As a result, it seems to me that, rather than generalize lambdas into
                        "full" anonymous functions (with most of the negatives and few of the
                        positives of lambda), it would be much better to specialize them further
                        into inline-closure-creators, where they can serve a valuable purpose
                        without quite as much risk of code pollution.

                        Jeff Shannon
                        Technician/Programmer
                        Credit International

                        Comment

                        • Bengt Richter

                          #13
                          Re: Securing a future for anonymous functions in Python

                          On Thu, 30 Dec 2004 15:15:51 -0800, Jeff Shannon <jeff@ccvcorp.c om> wrote:
                          [color=blue]
                          >David Bolen wrote:
                          >[color=green][color=darkred]
                          >>>I think this specific use case -- defining callbacks -- should be
                          >>>addressed, rather than proposing a solution to something that isn't
                          >>>necessary. (...)
                          >>>
                          >>>[/color]
                          >>
                          >>I'd be interested in this approach too, especially if it made it simpler
                          >>to handle simple manipulation of callback arguments (e.g., since I often
                          >>ignore a successful prior result in a callback in order to just move on
                          >>to the next function in sequence).
                          >>
                          >>[/color]
                          >
                          >It seems to me that what most people *actually* want, when asking for
                          >lambdas, is a quicker and more convenient way to get closures. (At
                          >least, that's what the vast majority of examples of lambda use seem to
                          >be for.) Perhaps one could get a bit more traction by looking for
                          >improved closure syntax instead of focusing on the anonymous function
                          >aspect.[/color]
                          Not sure what you mean by closure here. To me it means the necessary
                          external environment needed to be captured for use by a function definition
                          getting exported from its definition environment. I.e., it is something
                          a function uses, and part of the function definition, but it isn't the
                          function itself. I would compare a closure more to a callable class instance's
                          self attributes, except that the latter are more flexible.

                          In fact, for a callback, a constructor call creating a suitable
                          callable class instance could sometimes work well as a substitute
                          for a lambda expression, ISTM. (I.e., when it is not important to
                          show the code in line, and the differences are in initialization parameters
                          rather than code).
                          [color=blue]
                          >
                          >All of the suggestions for anonymous multiline functions (with embedded
                          >indentation) seem to me to miss what seems to me to be the only
                          >significant benefit of lambda -- its ability to be used in-line without
                          >creating a huge ugly tangle. (I'd argue that lambdas create a *small*
                          >ugly tangle, but apparently that's just me. ;) ) Lambdas do have some
                          >value in defining callbacks, but that value derives almost exclusively
                          >from the fact that they are in-line (rather than a few lines above).[/color]
                          They do let you define _code_ inline, which a constructor call doesn't do
                          (unless you pass a string to compile etc -- not cool).
                          [color=blue]
                          >Mimicking function-def indentation inside of another function's arglist
                          >strikes me as an abomination just waiting to happen; in comparison, the
                          >need to type a name twice seems trivial.[/color]
                          Self-restraint can avoid abominations ;-)
                          [color=blue]
                          >
                          >As a result, it seems to me that, rather than generalize lambdas into
                          >"full" anonymous functions (with most of the negatives and few of the
                          >positives of lambda), it would be much better to specialize them further
                          >into inline-closure-creators, where they can serve a valuable purpose
                          >without quite as much risk of code pollution.[/color]

                          There's always the temptation to be enforcer when being persuader
                          is not the easiest ;-)

                          (BTW, again, by closure, do you really mean deferred-action-thingie?)

                          Regards,
                          Bengt Richter

                          Comment

                          • Jeff Shannon

                            #14
                            Re: Securing a future for anonymous functions in Python

                            Bengt Richter wrote:
                            [color=blue]
                            > On Thu, 30 Dec 2004 15:15:51 -0800, Jeff Shannon <jeff@ccvcorp.c om> wrote:
                            >[color=green]
                            >>Mimicking function-def indentation inside of another function's arglist
                            >>strikes me as an abomination just waiting to happen; in comparison, the
                            >>need to type a name twice seems trivial.[/color]
                            >
                            > Self-restraint can avoid abominations ;-)[/color]

                            It can, but given the prevalence of lambda abominations (such as the
                            many that the Martellibot described among Cookbook submissions), is
                            there any reason to believe that there would be *more* restraint in
                            using a less-constrained feature?? :)
                            [color=blue][color=green]
                            >>As a result, it seems to me that, rather than generalize lambdas into
                            >>"full" anonymous functions (with most of the negatives and few of the
                            >>positives of lambda), it would be much better to specialize them further
                            >>into inline-closure-creators, where they can serve a valuable purpose
                            >>without quite as much risk of code pollution.[/color]
                            >
                            > (BTW, again, by closure, do you really mean deferred-action-thingie?)[/color]

                            My understanding of "closure" (which may well be wrong, as I've
                            inferred it entirely from past discussions on this newsgroup) is that
                            it's a callable with some or all of its parameters already set; e.g.,

                            def one_and(foo):
                            def closure(arg):
                            return foo(1, arg)
                            return closure

                            incr = one_and(operato r.add)

                            The function one_and() returns a closure, here bound to incr. It is
                            essentially a (partially) deferred action thingy, if you want to use
                            technical terms. ;) I suppose that one could look at it as the
                            environment in which to call a given function, exported for later use...

                            My thesis here is that one of the most common (legitimate) uses of
                            lambda is as an adapter, to create an intermediary that allows a
                            callable with a given signature to be used in places where a different
                            signature is expected -- that is, altering the number or order of
                            arguments passed to a given callable (and possibly also capturing the
                            current value of some other variable in the process). I feel that
                            it's more fruitful to focus on this "adapter" quality rather than
                            focusing on the "anonymous function" quality.

                            Jeff Shannon
                            Technician/Programmer
                            Credit International

                            Comment

                            • Nick Coghlan

                              #15
                              Re: Securing a future for anonymous functions in Python

                              Bengt Richter wrote:[color=blue]
                              > This is an easy trap to fall into, so if the new lambda-substitute could
                              > provide a prettier current-closure-variable-value capture than passing a dummy default
                              > value or nesting another def and passing the value in, to provide a private closure for each,
                              > that might be something to think about.[/color]

                              I forgot about that little trap. . .

                              Cheers,
                              Nick.

                              --
                              Nick Coghlan | ncoghlan@email. com | Brisbane, Australia
                              ---------------------------------------------------------------

                              Comment

                              Working...