Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

chaining method calls

by perrin (Chancellor)
on Jun 11, 2003 at 23:04 UTC ( [id://265214]=perlmeditation: print w/replies, xml ) Need Help??

The popular SOAP::Lite module chains method calls together:

print SOAP::Lite -> uri('http://www.soaplite.com/Temperatures') -> proxy('http://services.soaplite.com/temper.cgi') -> f2c(32) -> result;
I find this totally confusing and not at all intuitive. Why on earth would an object return a copy of itself in response to a setter? It makes no sense. It also seems like it would make debugging harder because it jams all of these method calls onto a single line, albeit a line with whitespace.

I know that I can call these methods in the normal way, but all the documentation, including the O'Reilly books, is written with this style. I have also noticed that this style is spreading. Ovid is using it in his new HTML parsing module.

So I'm curious: am I the only one who thinks this style is terrible? Do other people all love it?

Replies are listed 'Best First'.
Re: chaining method calls (smart error object)
by tye (Sage) on Jun 11, 2003 at 23:39 UTC

    The only real problem I have with it is that error checking (or debugging) can be a pain, as you noted. (update: That is, doing "nice" error checking doesn't allow this while debugging with it is confusing.)

    But the solution to that is to have these methods, on failure, return an "error object" that uses AUTOLOAD to return itself no matter what method gets called on it, in a string context gives back the descriptive error that it was created with (which should include method names and line numbers and such), in a boolean context returns a false value, and, if it gets destroyed without having been used in a boolean or string context, it dies with the descriptive error.

    I been wanting to write that "error object" for a few years now. (: Just haven't found the time/motivation/prioritization. ):

                    - tye

      I like your idea, enough even to have a bash at it myself, moreso for educational purposes than anything. The first thing that struck me thought was "How can AUTOLOAD determine boolean context?". I know that wantarray will define whether the calling context is list, scalar or void. However I am stumped when it comes to determining boolean context.

      If I have you right.., and Some::Package returns error objects on failure.

      my $obj = Some::Package->new( %params ); $obj->connect()->synchronise()->disconnect();
      Presuming the new method fails, returning an error object (populated with some meaningful message), then the call to connect will be caught by the error object's AUTOLOAD. At this point we determine context, and depending on that confess.

      Is there nothing already out-there to do this? I'm off to CPAN first. ++tye


      I can't believe it's not psellchecked
        The first thing that struck me thought was "How can AUTOLOAD determine boolean context?".

        AUTOLOAD doesn't have to. You overload bool and use it to set some internal flag in the object to say it's been evaluated in a boolean context. Then in DESTROY you throw an exception if the flag has not been set.

Re: chaining method calls
by Ovid (Cardinal) on Jun 11, 2003 at 23:36 UTC

    When chaining method calls, an mutator only returns the object if the method call succeeds. As a result, it doesn't inhibit debugging because, in your example, if the proxy() method fails, you'll get a fatal error stating "Can't call method f2c on an undefined value". This does two things. First, it tells you that the proxy() method failed. Second, you no longer have to check whether or not each and every mutator was successful. By failing to return a copy of the object, you immediately get a fatal error.

    It also has a nifty "dual use" feature: if you don't like chaining method calls, you don't have to. You can simply see if the method returns true or false upon success. Of course, checking each and every method can be a pain, so not everyone does it. However, if you try to set the day of the week to "9" or the color to "cheese", you have to check the method calls. Chaining gets this check for free, often even if you forget to add nice error checking in the method :)

    Cheers,
    Ovid

    New address of my CGI Course.
    Silence is Evil (feel free to copy and distribute widely - note copyright text)

      Hmm, wouldn't this normally be handled by throwing an exception rather than returning undef? I always set 'RaiseError' on DBI for this reason.

        Yes, that's a better way of going about it. I'll confess that when I'm coding things quickly, I often do the simplest thing and just return the undef rather than raise an exception. In my tests, I get a chance to evaluate this how this actually works and when I have problems, I go back in and croak, carp, or silently ignore the issue, depending upon what it is and how it affects the actual problem.

        That being said, I really like tye's error object idea. If I can create a class that has an Exception object, no methods, and an AUTOLOAD, I can get robust error handling with plenty of flexibility. And again, if someone doesn't like chained calls, they still don't have to use them.

        Cheers,
        Ovid

        New address of my CGI Course.
        Silence is Evil (feel free to copy and distribute widely - note copyright text)

Re: chaining method calls
by mirod (Canon) on Jun 11, 2003 at 23:16 UTC

    I actually love chaining methods (I changed a whole lot of methods in XML::Twig so they would return something that can be chained).

    What's not to love about being able to write:

    ... # handler for warning warning => sub { $_->change_tag( 'p') ->set_att( style => "color:red") ->set_prefix( "WARNING: "); }, ...

    (apart from the fact that you could do all of this with CSS of course ;--) ?

    I agree that sometimes it can get a little confusing though, when the returned object/value is not obvious: should $elt->delete_atts return the element, so you can chain method calls on it, or should it return the attribute hash so you can store it?

    Overall I think it makes for elegant code. You'll get use to it ;--)

Re: chaining method calls
by adrianh (Chancellor) on Jun 11, 2003 at 23:20 UTC

    I'm in the "love it" camp :-)

    It reads well to me. I just read "->" as "and then call method" and it makes perfect sense. It's compact, which is good in my book since it allows me to get more "useful" text on my screen when I'm coding.

    Never had problems debugging it myself.

    It's also similar to the normal Smalltalk idiom so people with that sort of background will find it natural.

      This is an important point on idiom, and since idiom is a matter of choice of expression, I won't weigh one way or the other, but will caution that Perl's ruthless lack of discipline, while a benefit in many cases, complicates things for those who prefer or need consistent design.

      In Perl, there's no difference between a "message" to an object and a "functor" answered by an object. Thus, obfuscation aside, they both have to use the same invocation syntax: $a->b().

      The following distinction is my own OO philosophy, sometimes consistent and sometimes inconsistent with others I've seen, but I think it's a very useful paradigm.

      Messages should be invoked for side-effects, and should have no immediate return value, so that (1) they can be transaction-wrapped, (2) they never block the invoker, and (3) on some application architectures they can be posted onto a queue instead of handled immediately. If you stick to the "messages have no immediate return value" philosophy, then supporting it with a Perl return $self idiom makes sense.

      Functors should not have side-effects (other than local caching), and should have an immediate return value. Again, these benefits complement message handlers: (1) they can be seen as atomic, (2) thus they may need to block, and (3) in a remote architecture, a dumb stub can return cached values but can't actually decide anything behaviorwise.

      (Some people may think "ack! invoke for side effects which may not be done immediately?" If you must wait for a message transaction to be realized or verified, you wait with a functor that can block appropriately. Otherwise, you trust it will get done.)

      Unfortunately, you can't easily tell a Perl "functor" from a Perl "message handler." They're both just subs. They're both invoked with $a->b(). If nobody makes a mistake, messages can chain with $self, but yet functors can't. Tread carefully.

      --
      [ e d @ h a l l e y . c c ]

Re: chaining method calls
by chromatic (Archbishop) on Jun 11, 2003 at 23:32 UTC

    I like it. It feels very Smalltalky. You're sending a bunch of messages to the object, so why repeat the invocant? Expressions have results, so why not use them?

    I co-opted this in Test::MockObject quite some time ago. It works well for accessors there. I don't know if I'd use it elsewhere.

      I like it. It feels very Smalltalky. You're sending a bunch of messages to the object, so why repeat the invocant? Expressions have results, so why not use them?

      In Smalltalk this is called a "cascade", and there's special syntax to support it. In Smalltalk, if you send two messages to the object anObject (i.e., if you invoke two of anObjects's methods), you can use cascading to rewrite

      anObject foo. anObject bar.
      as
      anObject foo; bar.
      Both messages get sent to anObject, regardless of what the first method returns. That is, the return value from foo is ignored. The value of this expression is whatever is returned from bar.

      Perl has no notion of method call cascading, so to simulate cascading, all member functions in a simulated cascade chain (except that final one) need to return self. This works great if you're making a chain of calls for side-effects, and less great if you're composing an expression from a set of cascaded calls, and are interested in the final value, because of the requirement that all cascadable methods return self.

      My opinion is that cascading is an idiom that doesn't translate well into Perl. Without a special syntax to designate a cascade, a simulated cascade is indistinguishable from a gross violation of the Law of Demeter.

        My opinion is that cascading is an idiom that doesn't translate well into Perl. Without a special syntax to designate a cascade, a simulated cascade is indistinguishable from a gross violation of the Law of Demeter.

        That's an interesting point, and the most convincing one I've come across against the idiom. I can see how a maintainence coder might see a cascade and have it look like a ghastly exposure of internals ripe for refactoring.

        That said, it's not a problem I've ever come across. Probably because that by the time you're looking at the code you already have an idea of what methods are associated with what objects, so you can mentally parse out cascades from other code.

        The fact that I try not to write objects that flagrently violate the LoD probably helps too :-)

        (I wonder if Perl6 will have an explicit syntax? If not we could always make one up.)

        I still find:

        $my_object_variable->foo($x)->bar($y)->ni($z);

        clear, and preferable to:

        $my_object_variable->foo($x) $my_object_variable->bar($y) $my_object_variable->ni($z) # or for ($my_object_variable) { $_->foo($x); $_->bar($y); $_->ni($z); };

        To me the former is succinct and obscures the mainline less. Probably too much Smalltalk at an early age :-)

Re: chaining method calls
by samtregar (Abbot) on Jun 12, 2003 at 06:15 UTC
    I'm definitely not a fan of this call-style. My prefered syntax for accessor/mutators is to have a single method which sets the value if passed a parameter and always returns the current value. This yields calls that read easily and behave in a consistent manner. If $train->speed returns the speed of the train why would $train->speed(10) return the train? It only makes sense if you're already familar with the syntax hack it supports, which isn't even in common usage! Hello maintainance nightmare.

    I find the argument (put forth by others) that returning an object on success or undef on failure aids debugging to be totally bogus. Let's take your example:

    print SOAP::Lite -> uri('http://www.soaplite.com/Temperatures') -> proxy('http://services.soaplite.com/temper.cgi') -> f2c(32) -> result;

    What happens if f2c() fails? Fatal error with a strange error message. What happens if result() fails? Nothing! Maybe you get a warning, but you certainly don't get an error. It's much, much better if mutators croak() if they can't perform their duty. Then error checking is entirely optional, yet still available via eval{}.

    -sam

    PS: All that said, I'm still a big fan of SOAP::Lite. It's got its quirks, but it gets the job done. To all the SOAP::Lite haters in the audience (and I know you're out there) I'm eagerly awaiting your complete SOAP implementations!

      It only makes sense if you're already familar with the syntax hack it supports, which isn't even in common usage! Hello maintainance nightmare.

      In the code I've been dealing with it seems to be about a 50/50 split between those who do or don't use the idiom. Possibly the codebases I've been dealing with are biased but in my experience the idiom is in fairly common usage.

      What happens if result() fails? Nothing! Maybe you get a warning, but you certainly don't get an error. It's much, much better if mutators croak() if they can't perform their duty. Then error checking is entirely optional, yet still available via eval{}.

      While I feel that tye's proposal is a little too clever for it's own good - you would get an error if the method failed.

      The method would return an error object on failure. This error object would not get evaluted in a boolean context. On GC the DESTROY method would then croak.

      While it's not a strategy I'd use (sorry tye - not convinced ;-) you would get an error.

      If you use an exception throwing style of error handling then it's not a problem at all.

        You don't even think timely, well-ordered destructors are important. So what do you know? ;)

        The idea is very DWIM and so I think it fits well with Perl. If Perl had better standardization/tools for exception handling, then I'd likely use that much more instead. As is, exception handling in Perl is what I consider to be an "uncommon practice" and likely to surprise people so I usually avoid it (and I regretted one case I can think of where I didn't and in the next release of the module I removed exception throwing).

        Because Perl has timely destructors, I don't mind using them. I find them very powerful. There are things that can be done very cleanly in timely destructors that are just plain hard to do without (lock this and without having to remember to put in any code elsewhere, I know the lock will be released when I leave this block; allocate this exclusively and free when we are done with it).

        The idea of not having well-ordered destructors just boggles my mind. But that is for another thread.

        I also find the distinction between exception and failure (so return a false value) to be a pretty slippery one. One of my favorite things about this idea is that I don't have to decide. I enable you, the module user, to decide which one you want to use and to change your mind depending on the situation.

        I also like that it detects coding mistakes where you forget to check whether something failed or not. I really think all Perl's built-in functions should be capable of behaving this way (like use Fatal qw( :void ... ) almost does) at least optionally.

                        - tye
      What happens if result() fails? Nothing!
      Which is no less than what happens if it failed in traditional
      print $soap->result;
      style.

      Makeshifts last the longest.

        So, uh, we agree then. The argument that chaining helped debugging is false. Both styles have the same problems. Good!

        -sam

(jeffa) Re: chaining method calls
by jeffa (Bishop) on Jun 12, 2003 at 11:44 UTC
    I am not only in the 'love it' camp, i am also responsible for helping spread this:
    # stackable method calls: print DBIx::XHTML_Table ->new($data_source,$usr,$pass) ->exec_query('select foo,baz from bar') ->output() ;
    The first time i saw this (and it was in SOAP::Lite as well, BTW) i was a bit perplexed, but once i understood the convience it gave to the client, i loved it. Could it make debugging harder for the client? Sure! Answer? Don't use it!
    my $table = DBIx::XHTML_Table->new($data_source,$usr,$pass); $table->exec_query('select foo,baz from bar'); print $table->output();
    I went back over my CVS tree to see when i added the change, and what comments i added:
    Version 0.98 (12/31/2001)
    - all methods that normally returned void now
      return $self (this allows stacking methods)
    
    However, i must admit that the large number of folks who don't like this does impact me a bit. I will keep an eye out for bugs that 'stackable methods' might introduce, but i have honestly had no troubles with them, be it debugging or what not. The key is to use the 'plain old boring' style while testing.

    Finally, this 'stackable method' style is most likely only for certain OO candidates. DBIx::XHTML_Table (while feature loaded) is a very simple module. All methods serve to get the final output, and i think that is what makes chaining them okay for that module. Other modules might be better off sticking to individual calls.

    Damn good question again, perrin :)

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
Re: chaining method calls
by Juerd (Abbot) on Jun 12, 2003 at 07:02 UTC

    I also find methods that return the object extremely annoying. Of course it is okay to chain method calls, but imNSho, only if the object returned if different from the one used to call the method. That makes sense and is useful, even though that too is not ideal when debugging. I don't mind doing

    my $page = LWP::UserAgent->new->request(...)->content;
    or
    my ($name) = DBIx::Simple ->connect('dbi:SQLite:people') ->query('SELECT name FROM people WHERE email=? LIMIT 1', $email) ->list;
    , but I do dislike when a method returns its $_[0].

    The argument that I hear the most is that chaining methods like this reduces typing. While that is true, I think it's a very bad reason for actually doing so. Using only globals and no strict is another way to save three characters per first use. Not checking open's return value is another way of typing less. It seems that length reduction and bad style often go together.

    Juerd # { site => 'juerd.nl', plp_site => 'plp.juerd.nl', do_not_use => 'spamtrap' }

      The argument that I hear the most is that chaining methods like this reduces typing. While that is true, I think it's a very bad reason for actually doing so. Using only globals and no strict is another way to save three characters per first use. Not checking open's return value is another way of typing less. It seems that length reduction and bad style often go together.

      Using subroutines is a way to save characters. So are foreach loops. You can argue it both ways.

      In many cases I'd agree with Paul Graham that Succinctness is Power.

      I think this is going to be one of those issues that people can agree to disagree over. The idiom has always seemed very natural and clear to me.

Re: chaining method calls
by grantm (Parson) on Jun 12, 2003 at 10:07 UTC

    Colour me unconvinced too.

    I do find the idea of a set-property-method returning a reference to the object itself unintuitive and confusing.

    I've seen this suggested as a way to initialise a bunch of properties at object creation time. A far more common and readable approach is to pass the constructor a series of name => value pairs. That doesn't preclude the constructor calling relevant mutator methods to store those values if necessary.

    Also, as a general style for accessors, I prefer the common Perl idiom of a getter/setter method which always returns the property value. Rather than having separate methods for get and set. Users of certain other languages may sneer at this, but that's probably just sour grapes :-)

    Having said all that, I have found it useful sometimes for a method to return the object reference. Just recently I had an object wrapping a DBI statement handle. I needed a method that caused the object to fetch and store the data internally. It just seemed natural for this method to return a reference to the original object so I could do this:

    my $recordset = $self->db_query("select * from sometable")->prefetch;

    instead of:

    my $recordset = $self->db_query("select * from sometable"); $recordset->prefetch;

    The difference (at least in my mind) was that although the prefetch method was mutating the object, it wasn't a simple property setter and it would not otherwise have returned a value (it would raise an exception on error).

object aggregators
by mojotoad (Monsignor) on Jun 12, 2003 at 20:15 UTC
    I've used a related idiom in some of my modules -- namely, having a dispatch or aggregator object that operates on several objects simultaneously.

    So rather than:

    @parms = ...; foreach $obj (@objects) { $obj->duckwalk(@parms); }

    I just say:

    $ringmaster->aggregate(@objects)->duckwalk(@parms);

    If you wonder why you'd ever want to do that, imagine manipulating an HTML::Element tree that represents a table like so:

    # Manipulates multiple table cells $table->col(7)->attr('bgcolor','#BBFFBB');

    So to answer your original question, I like the idiom. I consider it a degenerate case of what I illustrated above.

    Matt

      Your second example is interesting, but I wouldn't call it method chaining at all: Method chaining is a mechanism to change related attributes, which have very little effect on each other. Consider:
      $person->make_head('large') ->make_nose('small') ->make_belly('surgerized');
      This is method chaining. What you are doing (externally), however, is calling a method on the seventh column of a table. It is best achieved simply (IMHO) by returning a reference to an object of package column, whose methods you can call (your probably knew this, but I'd rather emphasize the point for other readers). Code would be something like:
      package Table; use Column; sub new { bless [],__PACKAGE__ } sub col { return $_[0]->[$_[1] + 1] if exists $_[0]->[$_[1] + 1]; $_[0]->make_column([$_[1] + 1]) and return $_[0]->[$_[1] + 1]; } sub make_column { $_[0]->[$_[1]] = Column->new; }
      Like you said, you're even doing that in your first example. You're just enapsulating a series of objects in a greater object. The method I use, though, would make it so that you don't have to even hold the differant objects. ( sub aggregate { $_->duckwalk($_[1]) for @{$_[0]}) })

      There are no similarities between that and object chaining- you are not returning self; you are returning a different object...
      Aggregators are perfectly intuitive, because most methods return something. For aggregators that something returned is a different object, but that's no different from 5.squared returning 25, if you consider 5 and 25 to be objects of NUM or something like it (like they are in Ruby and other languages).

      However, method chaining is completely counterintuitive, because it's not natural to have $object->make_head('larger') return the object with the head made larger. One would expect it, like most methods, to return true on success and false on failure. This is the programming idiom that most programmers would subscribe too. If something is not returning true or false, then OO standards would dictate that it's returning another object, which will be modified. If you were to give me the same code I previously mentioned without telling me that you're employing method chaining, I'd ask you why you're modifying the belly of a nose. What is the belly of the nose?

      This point is hammered in by that notion. However, intuitively, my rejection of method chaining is that it doesn't make sense without the requisite white space. Programmers are almost forced to indent when they chain methods to show that all the methods are basically working off of the single parent object. When indentation is forced for a process so simple as changing an object you know there's a problem.
      Gyan Kapur
      gkapur@myrealbox.com

        However, method chaining is completely counterintuitive, because it's not natural to have $object->make_head('larger') return the object with the head made larger. One would expect it, like most methods, to return true on success and false on failure.

        Well, it does return true on success. And when you provide for method chaining you usually use exception handling and not inband error codes. As for indenting the chain, thats a matter of taste. All I know is that I sure am glad I can type:

        print Data::Dumper->new([$foo],[qw(foo)])->Indent(2)->Purity(2)->Dump( +);

        Instead of

        print do{my $d=Data::Dumper->new([$foo],[qw(foo)]); $d->Indent(2); $d- +>Purity(2); $d->Dump()};

        And any hard and fast rule that says I can't/shouldn't do the above I would reject as being utterly unworkable in practice.

        As I said elsewhere in this thread, the probable and common usage case should be the deciding factor in how a given method behaves. One shouldn't have to contort oneself to fit into some arbitrary coding scheme when there is no reason to. The Perl motto for things like this is that easy things should be easy and hard things possible. Selecting a method design that meets these requirements is the only design criteria as far as I am concerned.
        ---
        demerphq

        <Elian> And I do take a kind of perverse pleasure in having an OO assembly language...

      That's a great idea. I've always liked the XPath syntax to identify a set of nodes in a tree - so much more elegant and concise than any kind of iteration.

      Makeshifts last the longest.

Re: chaining method calls
by EvdB (Deacon) on Jun 12, 2003 at 09:22 UTC
    I am not a fan.

    Consider this code:

    use HugeObject; my $result = 0; { my $object = HugeObject->new(); $result = $object->get_even_bigger(); $object = undef; } # Phew, should free up memory now. if ($result) { # code here }

    As the object was created inside the {} it should go out of scope and the memory should be cleared - a good thing. However as object returns a reference to itself it is not cleared, and stays in memory.

    If the object had just returned true or false this would not be a problem.

    This may not be a problem if you read the docs for HugeObject but why add the compication for some slightly shorter code?

    --tidiness is the memory loss of environmental mnemonics

      If it's a method that is intended to return a boolean result then I would return a boolean. I only use chaining on methods that don't return a result.

      If a false result is intended to indicate an error, I would throw an exception instead - so I wouldn't need to store $result.

      For the style of code I write this problem wouldn't be an issue.

      (also, the $object=undef is redundant. It would be collected at the end of scope regardless.)

Re: chaining method calls
by talexb (Chancellor) on Jun 12, 2003 at 13:53 UTC

    Thanks to jeffa for explaining what the heck was going on -- until I read his node (the last on this page) I had no idea what you folks were talking about (again).

    This is very cool, but it would also make debugging a bit of a challenge -- who wants to re-write code to catch an intermediate result?

    While it is a little longer to say ..

    my $soap = uri('http://www.soaplite.com/Temperatures'); $soap->proxy('http://services.soaplite.com/temper.cgi'); $soap->f2c(32); print $soap->result;

    .. in terms of code maintenance that's much better (if I've translated that back correctly).

    --t. alex
    Life is short: get busy!
      Thanks to jeffa for explaining what the heck was going on -- until I read his node (the last on this page) I had no idea what you folks were talking about (again).

      You just put your finger on what I don't like about this: it is not a common idiom. It confuses many people, even those who know perl well.

        You just put your finger on what I don't like about this: it is not a common idiom.

        I keep seeing people say this idiom isn't common, which to me is a bit of a suprise as Data::Dumper employs this idiom, and even has

        $d->Purity(1)->Terse(1)->Deepcopy(1);

        in its synopsis, and

        The method forms return the object itself when called with arguments, so that they can be chained together nicely.

        in its method documentation. As Data::Dumper is one of my earliest memories of Perl (the wonder of watching that data structure stream down the screen!) I have always assumed this idiom was common place. (And shouldnt every Perl hacker past their first script know about Data::Dumper? The thought of hacking perl without it make me cringe.)

        Having said that, Ive employed the idiom myself on occasion, and on occasion found myself removing it afterwards. I think its an optimise-for-the-common-usage judgement call when designing a classes interface. If its likely that you would want to set many attributes of an object at once, without being concerned with the new or old value then it can be convenient to provide chaining. OTOH, sometimes I find that having set accessors return the new value (or the old value sometimes) can be a big code simplification, so sometimes I go that way. I do whatever "feels" appropriate for how I envisage myself and others using the routines in question. So long as the behaviour is documented I dont see any problem with any of the common approaches, nor with using any mixture of them in a single module, so long as the end result is reasonably intuitive.


        ---
        demerphq

        <Elian> And I do take a kind of perverse pleasure in having an OO assembly language...
Re: chaining method calls
by particle (Vicar) on Jun 20, 2003 at 14:50 UTC

    i'm a fan. it makes client interfaces easy to implement. and since i haven't seen it mentioned in this thread, i thought you might find Want of interest. context is everything!

    use Want; sub foo { my( $self, @args )= @_; $self->munge(@args); return $self if want('OBJECT'); return @args; }

    ~Particle *accelerates*

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://265214]
Approved by gmax
Front-paged by VSarkiss
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (2)
As of 2024-03-19 04:08 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found