AboutQuality AboutPerl AboutObjects AboutPatterns | SoftwareQualityLevels AboutFlack PlanningIsNpComplete |
AggregatePattern DecoratorPattern ProxyPattern AdapterPattern FacadePattern ResultObject VisitorPattern
ClassAsTypeCode StatePattern MomentoPattern
SingletonPattern CurryingConcept CloningPattern FlyweightPattern, ImmutableObject AbstractFactory FactoryObject RunAndReturnSuccessor
StateVsClass CommandObject IteratorInterface PassingPattern
WrapperModule AnonymousSubroutineObjects ConstraintSystem RevisitingNameSpaces
ObjectsAndRelationalDatabaseSystems SelfJoiningData ManyToManyRelationship OneToOneRelationshipsTurnIntoOneToManyRelationships BiDirectionalRelationshipToUnidirectional
NamedArguments PassingState FunctionTemplating AssertPattern CodeAsData NonReenterable SelectPollPattern JournalingPattern
WebAuthentication WebScraping ReadingAFile ConfigFile ErrorReporting ExtensibilityPattern
AboutRefactoring RefactoringPattern GeneralizePattern ExpressionsBecomeFunctions BreakDownLargeFunctions LocalVariablesReplaceGlobalVariables GlobalToLexical SoftrefsToHash TooManyVariables TooManyArguments MoveLargeDatastructuresToContainers MoveCollectionsOfFunctionsToObjects SuperClassAbstraction IntroduceNullObject AbstractRootClasses BiDirectionalRelationshipToUnidirectional
LooseCoupling TypeSafety DesignContract LayeringPattern FunctionalProgramming CurryingConcept ManyToManyRelationship
OtherStructuredSystems SelectCPANModules HowDoesPerlStackUp PerlDoc DocBook CvsQuickRef ClassNaming ReferencesMeta
PlanToThrowOneAway HowPerlDiffersFromC BasicPerlOOSyntax PerlOoNutsAndBolts
PerlPatternsResources ReferencesMeta GuestLog AboutPerlPatternsProject SkipTheIntroduction YetAnotherIntroduction AboutTheAuthorAll content on this server is copyright 2002, 2003 by ScottWalters, unless otherwise noted. Content credited otherwise is copyright its original author and has been generously made available by them under the same terms as the rest of the project, the GnuFreeDocumentationLicense. Member of CategoryBook.
FunctionalProgramming ShortHandInstanceVariables AccessorPattern TypeSafety TypedVariables PrivateFunctions AbstractClass AbstractFactory InnerClasses OverloadOperators ClassAsTypeCode ImmutableObject FlyweightPattern AnonymousSubroutineObjects ConfigFile FunctionTemplating TooManyArguments ConstraintSystem DecoratorPattern ExportingPattern GodObject MoveCollectionsOfFunctionsToObjects MoveLargeDatastructuresToContainers SharedData StateVsClass TooManyArguments TypedVariablesNewbies: follow a hyperlink as soon as you start to get lost. You'll wind up at the basics if you do this. Meta information is also at the end, including the GuestLog and information about the project itself.
my $subtotal; foreach my $item (@cart) { $subtotal += $item->query_price(); } my $weight; foreach my $item (@cart) { $weight += $item->query_weight(); } # and so onRepresenting individual objects when the application is concerned about the general state of several objects is an ImpedenceMismatch. This is a common mismatch as programmers feel obligated to model the world in minute detail then are pressed with the problem of giving it all a high level interface.
package Cart::Basket; @ISA = qw(Cart::Item); sub query_price { my $self = shift; my $contents = $self->{contents}; foreach my $item (@$contents) { } } # other query_ routines here... sub add_item { my $self = shift; my $contents = $self->{contents}; my $item = shift; $item->isa('Cart::Item') or die; push @$contents, $item; return 1; }The aggregation logic, in this case, totalling, need only exist in this container, rather than being strewn around the entire program. Less code, less CodeMomentum, fewer depencies, more flexibility.
# in a file named Taco.pm: package Taco; use ImplicitThis; ImplicitThis::imply(); sub new { bless { price=>5.95}, $_[0]; } sub query_price { return $price; } # in a file named TacoWithLettuce.pm: package TacoWithLettuce; use ImplicitThis; ImplicitThis::imply(); @ISA = qw(Taco); sub query_price { return $this->Taco::query_price() + 0.05; } # in a file named TacoWithTomato.pm: package TacoWithTomato; use ImplicitThis; ImplicitThis::imply(); @ISA = qw(Taco); sub query_price { return $this->Taco::query_price() + 0.10; } # in a file named TacoWithTomatoAndLettuce.pm: package TacoWithTomatoAndLettuce; use ImplicitThis; ImplicitThis::imply(); @ISA = qw(Taco); sub query_price { return $this->Taco::query_price() + 0.10; }To do it this way, they would have to create a class for each and every topping, as well as each and every combination of toppings! With two toppings this isn't out of hand. With 8 toppings, you've got 256 possible combinations. With 12 toppings, you've 4096 combinations. Creating a permanent inheritance is the root of the problem, here. If we could do something similar, but on the fly, we wouldn't need to write out all of the possible combinations in advance. We could also make the inheritance chain deeper and deeper as we needed to.
# in a file named Taco.pm: package Taco; use ImplicitThis; ImplicitThis::imply(); sub new { bless { price=>5.95, first_topping=>new Topping::BaseTaco }, $_[0]; } sub query_price { return $first_topping->query_price(); } sub add_topping { my $topping = shift; $topping->isa('Topping') or die "add_topping requires a Topping"; $topping->inherit($first_topping); $first_topping = $topping; } # in a file named Topping.pm: package Topping.pm; # this is just a marker class # in a file named Topping/BaseTaco.pm: package Topping::BaseTaco; @ISA = qw(Topping); sub query_price { return 5.95; } # in a file named Topping/Lettuce.pm: package Topping::Lettuce; @ISA = qw(Topping); use ImplicitThis; ImplicitThis::imply(); sub query_price { return 0.05 + $this->SUPER::query_price(); } sub inherit { my $parent = shift; unshift @ISA, $parent; return 1; } # and so on for each topping...The astute reader will notice that this isn't much more than a linked list. Since inheritance is now dynamic, we've gotten rid of needing to explicit create each combination of toppings. We use inheritance and a recursive query_price() method that calls its parent's version of the method. When we add a topping, we tell it to inherit it from the last topping (possibly the base taco). When someone calls query_price() on the taco, we pass off the request to our first topping. That topping passes it on down the line, adding them up as it goes.
# in a file named Taco.pm: package Taco; use ImplicitThis; ImplicitThis::imply(); sub new { bless { price=>5.95, top_topping=>new Topping::BaseTaco }, $_[0]; } sub query_price { return $price; } sub add_topping { my $new_topping = shift; # put the new topping on top of existing toppings. this new topping is now our top topping. $new_topping->top($top_topping); $top_topping = $new_topping; return 1; } # in a file named Topping.pm: package Topping.pm; use ImplicitThis; ImplicitThis::imply(); sub new { my $type = shift; bless { we_top=>undef }, $type; } sub top { my $new_topping = shift; $new_topping->isa('Topping') or die "top must be passed a Topping"; $we_top = $new_topping; return 1; } # in a file named Topping/BaseTaco.pm: package Topping::BaseTaco; @ISA = qw(Topping); sub query_price { return 5.95; } # in a file named Topping/Lettuce.pm: package Topping::Lettuce; use ImplicitThis; ImplicitThis::imply(); @ISA = qw(Topping); sub query_price { return 0.05 + ($we_top ? $we_top->query_price() : 0); }There! We finally have something that passes as workable! This solution is good for something where we want to change arbitrary features of the object without the containing object (in this case, taco) knowing before hand. We don't make use this strength in this example. Since the query_price() method of the taco object just passes the request right along, we can do any math we want. We would be using this strength if we had a two-for-taco-toppings-Tuesday, where all toppings were half price on Tuesdays. With a press of a button, a new object could be pushed onto the front of the list that defined a price method just returned half of whatever the price_method() in the next object returns. The important thing to note is that we can stack logic by inserting one object in front of another when "has-a" relationships.
package GenericProxy; sub new { my $type = shift; my $this = { }; my $obj = shift; ref $obj or die; $this->{'obj'} = $obj; $type .= '::' . ref $obj; # copy inheritance info. @{ref($this).'::ISA'} = @{ref($obj).'::ISA'}; bless $this, $type; } # bug XXX - autoload is only used after @ISA is searched! sub AUTOLOAD { my $this = shift; (my $methodName) = $AUTOLOAD m/.*::(\w+)$/; return if $methodName eq 'DESTROY'; $this->{'obj'}->$methodName(@_); }This simple idea has many uses:
$foo->do($arg, $str, $bleah, $blurgh);Should the arguments do() accepts be changed, every place it is called would need to be changed as well to be consistent. Failure to do so results in no warning and erratic bugs. TypeSafety helps, but this is still no compile time check - missing an a call can lead a program killing bug.
foreach my $class ( qw(NAME SYNOPSIS CODE) ) { no strict 'refs'; push @{ "POD::${class}::ISA" }, "POD::POD"; }Not having to use a different method call in each behavior object is key. That would prvent us from using them interchangably. It would introduce need for hardcoded dependencies. We would no longer be able to easily add new behavior objects. Assuming that each behavior object has exactly one method, each method should have the same name. Something generic like ->go() is okey, I suppose. Naming it after the data type it operators on makes more sense, though. If there is a common theme to the behavior objects, abstract it out into the name. ->top_taco() is a fine name.
package Taco::Topper; sub top_taco { my $self = shift; die "we're an abstract class, moron. use one of our subclasses" if ref $self eq PACKAGE__; die "method strangely not implemented in subclass"; } sub new { my $class = shift; bless [], $class; } package Taco::Topper::Beef; sub top_taco { my $self = shift; my $taco = shift; if($taco->query_flags()) { die "idiot! the beef goes on first! this taco is ruined!"; } $taco->set_flags(0xdeadbeef); $taco->set_cost($taco->query_cost() + 0.95); } package Taco::Topper::Cheese; sub top_taco { my $self = shift; my $taco = shift; if(! $taco->query_flag(0xdeadbeef) and ! $taco->query_flag(0xdeadb14d)) { # user is a vegitarian. give them a sympathy discount because we feel # bad for them for some strange reason, even though they'll outlive us by 10 years $taco->set_cost($taco->query_cost() - 1.70); } $taco->set_flags(0xc43323); $taco->set_cost($taco->query_cost() + 0.95); } package Taco::Topper::Gravey; # and so on...Gravey? On a taco? Yuck! In real life, places in the mall that serve "tacos" also tend to serve fries, burgers, hotdogs, and other dubiously non-quasi-Mexican food. It doesn't make sense to have one vat of cheese for the nachos, another for tacos, and yet another for cheesy-gravey-fries. The topper should be able to apply cheese to any of them. Keep in mind that these behavior classes work on a general class of objects, not merely one object. A burger could be a subclass of a taco. See StateVsClass for some thoughts on what makes a good subclass.
$topping_counter->get_cheese_gun()->top_taco($self);... where $topping_counter holds our different topping guns, and get_cheese_gun() returns a cached instance of Taco::Topper::Cheese. This creates a sort of a cow-milking-itself problem. The taco shouldn't be cheesing itself, some other third party should make the connection. Assuming that the topping counter has been robotized and humans enslaved by the taco craving robots, perhaps the topping counter could cheese the taco. [13].
# using TypeSafety: sub set_day { die unless $_[0]->isa('Day'); $day = shift; return 1; } # using a plain old hash: sub set_day { die unless exists $daysref->$_[0]; $day = shift; return 1; }Everything from this set passes the "isa" test, so we can use TypeSafety to check our arguments. In any other language, it would be impossible to add to the set after being created this way, but we could do revisit the package (see RevisitingNamespaces) or redefine the constructor in Perl, so this shouldn't be considered secure.
package Day; use ImplicitThis; ImplicitThis::imply(); $mon = new Day 'mon'; $tues = new Day 'tues'; my @days; sub new { die unless caller eq PACKAGE__; my $me = { id=>$_[1] } bless $me, $_[0]; push @days, $me; return $me; } sub get_id { return $id }; sub get_days { return @days; } # in Apopintment.pm: package Appointment; my $day; sub set_day { die unless $_[0]->isa('Day'); $day = shift; return 1; }XXX examples of use, what you can and cannot do, etc.
$mon eq $mon; # true $mon eq $tues; # falseThis behavior, too, is shared with the SingletonPattern. The same effect could be acheived using OverloadOperators. This approach is simplier and more clear.
package Pocket::Computer; sub record_audio { # implemented in some subclasses but not others } sub take_a_memo { # that we can do } sub make_a_call { die "don't know how, and the FCC would have a cow"; } package Pocket::Phone; sub record_audio { # some do, some don't. most don't. } sub take_a_memo { die "i'm not a PDA"; } sub make_a_call { # this we can do }Some devices can do some things, others can do other things. Each device does not have to check to see if it is the kind of device that can - it just knows, because thats what it is, and identity is a large part of ObjectOrientation.
At a certain level of complexity the concept of a StateChange is introduced. Cars suffer from this complexity. You may go from parked to idling, or you may go from idling to accelerating, but not from parked to accelerating. Going from accelerating to parked is also known as an insurance claim. Each state knows the states that are directly, immediately attainable. BreadthFirstRecurssion or DepthFirstRecurssion is needed to plan out anything more complex.
package Memento; sub new { my $type = shift; my %opts = @_; die PACKAGE__ . " requires an object passed on its constructor: new Memento object=>\$obj" unless $opts{'object'}; my $this = { object=>$opts{'object'}, checkPoint=>undef }; bless $this, $type; } sub mementoCheckPoint { my $this = shift; $this->{'checkPoint'} = $this->deepCopy($this->{'object'}); } sub mementoRestore { my $this = shift; $this->{'object'} = $this->{'checkPoint'}; } sub AUTOLOAD { my $this = shift; (my $method) = $AUTOLOAD =~ m/.*::(\w+)$/; return if $method eq 'DESTROY'; return $this->{'object'}->$method(@_); } sub deepCopy { my $this = shift; my $ob = shift; die unless caller eq PACKAGE__; # private return $ob if(!ref $ob); if(ref $ob eq 'SCALAR') { my $value = $$ob; return \$value; } if(ref $ob eq 'HASH') { my %value = %$ob; return \%value; } if(ref $ob eq 'ARRAY') { my @value = @$ob; return \@value; } # FILEHANDLE, GLOB, other cases omitted # assume its an object based on a hash # XXX man perlfunc say that $ob->isa('HASH') works...? my $type = ref $ob; my $newself = { }; foreach my $i (keys %$ob) { $newself->{$i} = $this->deepCopy($ob->{$i}); } return $newself; }While this is a generic Memento package, it cannot possibly know how to correctly deal with objects contained inside the object given it. A version of this (possibly subclassed) tailored to a specific package would handle this situation correctly. Here, we replicate objects mercilessly. This code also violates the encapsulation rules of OO. Use it as a starting point for something that doesn't.
package MountRushmore; my $oneTrueSelf; sub new { if($oneTrueSelf) { return $oneTrueSelf; } else { my $type = shift; my $this = {presidents => ['George Washington', 'Thomas Jefferson', 'Theodore Roosevelt', 'Abraham Lincoln'] }; $oneTrueSelf = bless $this, $type; return $this->new(); } } sub someMethod { ... }Singletons are a special case of StaticObjects+.
package Roulette::Table; sub new { my $class = shift; my $this; # if new() is called on an existing object, we're providing additional # constructors, not creating a new object if(ref $class) { $this = $class; } else { bless $this, $class; } # read any number of and supported type of arguments foreach my $arg (@_) { if($arg->isa('Roulette::Color')) { $this->{'color'} = $arg; } elsif($arg->isa('Roulette::Number')) { my $numbers = $this->{'numbers'}; push @$numbers, $arg; } elsif($arg->isa('Money')) { if($this->{'money'}) { $this->{'money'}->combine($arg); } else { $this->{'money'} = $arg; } } } return $this; } sub set_color { new(@_); } sub add_number { new(@_); } sub add_wager { new(@_); }The constructor, new(), accepts any number or sort of object of the kinds that it knows about, and skuttles them off to the correct slot in the object. Our set routines are merely aliases for new(). new() may be called multiple times, directly or indirectly, to spread our wager over more numbers, change which color we're betting on, or plunk down more cash. I don't play roulette - I've probably butched the example. Feel free to correct it. Use the little edit link. People won't be doing everything for you your entire life, atleast I hope.
package Roulette::Table; use MessageMethod; sub new { my $class = shift; my $this; my $curry; bless $this, $class; $curry = MessageMethod sub { my $msg = shift; if($msg eq 'spin_wheel') { die "Inconsistent state: not all arguments have been specified"; } if($msg eq 'set_color') { $this->{'color'} = shift; } if($msg eq 'add_number') { $this->{'numbers'} ||= []; my $numbers = $this->{'numbers'}; push @$numbers, $arg; } if($msg eq 'add_add_money') { if($this->{'money'}) { $this->{'money'}->combine($arg); } else { $this->{'money'} = $arg; } } if($msg eq 'is_ready') { return 0; } if($this->{'money'} and $this->{'color'} and $this->{'numbers'}) { return $this; } else { return $curry; } }; return $curry; } sub spin_wheel { # logic here... } sub is_ready { return 1; }This second example doesn't support repeated invocations of new() to further define an unfinished object. It could, but it would detract from the example. Add it for backwards compatability if for any reason. More radically, we don't accept any constructors. We return an entirely new object that has the sole purpose of accepting data before letting us at the actual object.
sub create_roulette_table { my $color; my $money; my $numbers; return sub { $color = shift; return sub { $money = shift; return sub { push @$numbers, shift; return sub { # play logic here }; }; }; }; } # to use, we might do something like: my $table = create_roulette_table()->('red')->('500')->(8); $table->(); # play $table->(); # play again # or we might do something like: my $table_no_money = create_roulette_table()->('red')->('500'); my $table; $table = $table_no_money->(100); $table->(); # play $table->(); # play again -- oops, lost everything $table = $table_no_money->(50); $table->(); # play some moreThis is stereotypical of currying as you'd see it in a language like Lisp. The arguments are essentially untyped, so we take them one at a time, in a specific order. Also like Lisp, the code quickly migrates across the screen then ends aburptly with a large number of block closes (the curley brace in Perl, paranthesis in Lisp). The Lisp version makes heavy use of RunAndReturnSuccessor. If we wanted to adapt this logic to spew out GeneratedMethods, where each method generated wasn't tied to other generated methods, we would need to explicitly copy the accumulated lexical variables rather than simply binding to them. For example, my $color = $color; my $money = shift; would prevent each anonymous routine returned from sharing the same $color variable, although without further logic, they would all have the same value. This amounts to the distinction between instance and class data.
package Mumble; sub new { ... }; # standard constructor sub clone { my $self = shift; my $copy = { %$self }; bless $copy, ref $self; };Note that this is a ShallowCopy+, not a DeepCopy+: clone() will return an object that holds additional references to things that the object being copied holds onto. If it were a DeepCopy+, the new copy would have it's own private copies of things. This is only an issue when the object being copied refers to other objects, perhaps delegating to them. A DeepCopy+ is a recursive copy. It requires that each and every object in this network implement ->clone(), though we could always fall back on reference sharing and fake it.
my $copy = { %$self };%$self expands the hash reference, $self, into a hash. This is done in a list context, so all of the key-value pairs are expanded returned out - this is done by value, creating a new list. This happens in side of the { } construct, which creates a new anonymous hash. This is assigned to $copy. $copy will then be a reference to all of the same data as $this, The end result is a duplicate of everything in side of $self. This is the same thing as:
sub clone { my $self = shift; my $copy; foreach my $key (keys %$self) { $copy->{$key} = $self->{$key}; } bless $copy, ref $self; }If we wanted to do a DeepCopy+, we could modify this slightly:
sub clone { my $self = shift; my $copy; foreach my $key (keys %$self) { if(ref $self->{$key}) { $copy->{$key} = $self->{$key}->clone(); } else { $copy->{$key} = $self->{$key}; } } bless $copy, ref $self; }This assumes that $self contains no hashrefs, arrayrefs, and so on - only scalar values and other objects. This is hardly a reasonable assumption, but this example illustrates the need for and implementation of recursion when cloning nested object structures.
package FooFlyweight; my $objectCache; sub new { my $type = shift; my $value = shift; # just a scalar if(exists $objectCache->{$type}->{$value}) { return $objectCache->{$type}->{$value}; } else { my $this = { value => $value, moonPhase=>'full' }; bless $this, $type; $objectCache->{$type}->{$value} = $this; return $this; } }This example returns an object if we have one for that type and value. If not, it creates one, and caches it. An observant reader will note that if we cache objects, give it to two people, and one person changes it, the other will be affected. There are two solutions: pass out read-only objects, or preferably, use ImmutableObjects+.
package TinyNumberOb; sub new { my $type = shift; my $value = shift; # scalar value my $this = \$value; # scalar reference bless $this, $type; } sub getValue { my $self = shift; return $$self; } sub setValue { my $self = shift; $$self = shift; return 1; }This is kind of like Perl's Autovivication of variables and hash and array entries: things spring into existance at the moment a user asks for them.
$number->add(10);You'll write instead:
$number = $number->add(10);Other modules using the old $number can continue doing so in confidence, while every time you change yours, you get a brand new one all your own. If your class is a blessed scalar, your add() method might look like:
sub add { my $me = shift; my $newval = $$me + shift; return bless \$newval, ref $me; }Returning new objects containing the new state is strictly required for overloading Perl operators. Java's String class (different than StringBuffer) are an example of this: you can never make changes to a String, but you can ask an existing String to compute a new String for you.
package Car::Factory; sub create_car { my $self = shift; my $passengers = shift; my $topspeed = shift; return new Car::Ford if $topspeed < 100 and $passengers >= 4; return new Car::Honda if $topspeed < 120 and $passengers <= 2; return new Car::Porsche if $topspeed > 160 and $passengers <= 2; # etc } # in main.pl: package main; use Car::Factory; my $car = Car::Factory->create_car(2, 175); $car->isa('Car') or die;To be OO "pure" (polymorphic) each kind of car should @ISA = (Car), so that they pass the $ob->isa('Car') test. This lets programs know that it is a car (reguardless of kind) and can thus be used interchangably.
package Car::Factory; sub create_car { # this way we can do Car::Factory->create_car(...) or $carfactoryref->create_car(...) my $package = shift; $package = ref $package if ref $package; my $car = new Car::GenericAmericanCar; my $kind = shift; return bless $car, 'Car::Ford' if $kind eq 'ford'; return bless $car, 'Car::Dodge' if $kind eq 'dodge'; return bless $car, 'Car::Buick' if $kind eq 'buick'; return bless $car, 'Car::Pontiac' if $kind eq 'pontiac'; die "I don't think we make $kind in this country. Try Mexico."; }If you do something like that, you will prolly want Car::Ford to inherit Car::GenericAmericanCar with a line at the top reading @ISA = qw(Car::GenericAmericanCar Car) so that the methods continue to be available after you re-bless it (and so that they are marked as being a Car). Going hog wild, we could make our code do this for us:
package Car::Factory; sub create_american_car { my $package = shift; $package = ref $package if ref $package; my $car = new Car::GenericAmericanCar; my $kind = ucfirst(shift()); push @{$kind.'::ISA'}, 'Car', 'Car::GenericAmericanCar'; return bless $car, 'Car::' . $kind; }There! No matter what kind of car the user requests, we create it - even if it didn't exist before we created it. We set the @ISA array to inherit from Car and Car::GenericAmericanCar. Even if the package was completely empty, it now contains the minimal amount of definition to make it useful: an inheritance. You probably don't want to do exactly this, unless you really want the same product rebadged with a bizarre variety of different names.
my $factory = new FordFactory; my $wifes_car = $factory->create_car(); $wifes_car->isa('Car') or die; # later: $factory = new ChevyFactory; my $husbands_car = $factory->ChevyFactory; $husbands_car->isa('Car') or die;Code need not be concerned with where the cars come from, only that a Car materialize upon demand. Having a second source available for things is important. If there were only one auto manufacturer, a lot fewer people would be happy with their ride. Ralph Nader never would have won a law suit against them. The same goes for programs. Hacking up an entire program to change which implementation you use is undesireable. Sometimes you have an implementation you really want to get rid of.
See also: http://patternsinperl.com/designpatterns/factorymethod/
# Non ObjectOriented: my $parser = do { my $html; # HTML to parse my $tag; # name of the current HTML tag my $name; # name of current name=value pair we're working on my $namevalues; # hashref of name-value pairs inside of the current tag my $starttag = sub { if($html =~ m{\G(<!--.*?-->)}sgc) { return $starttag; } if($html =~ m{\G<([a-z0-9]+)}isgc) { $tag = $1; $namevalues = {}; return $middletag; } if($html =~ m{\G[^<]+}sgc}) { return $starttag; } return undef; }; my $middletag = sub { if($html =~ m{\G\s+}sgc) { return $middletag; } if($html =~ m{\G<(/[a-z0-9]*)>}isgc) { $name = $1; return $middlevalue; } if($html =~ m{\G>}sgc) { $namevalues->{$name} = 1 if $name; return $starttag; } return undef; }; my $middlevalue = sub { if($html =~ m{\G=\s*(['"])(.*?)\1}isgc) { $namevalues->{$name} = $1 if $name; return $middletag; } if($html =~ m{\G\s+}sgc) { return $middlevalue; } return $middletag; }; return sub { $html = shift; return $starttag; }; }; open my $f, 'page.html' or die $!; read my $f, my $page, -s $f; close $f; $parser = $parser->($page); $parser = $parser->() while($parser);Of course, rather than iterating through $parser and using it as a generator, we could blow the stack and make it do the recursive calls itself. In general, return $foo; would be replaced with return $foo->();.
package Parrot; sub new { my $type = shift; my $me = { @_ }; bless $me, $type; } sub perch { my $this = shift; $this->{perch} = shift; $this->{perch}->add_weight(38); return 1; } sub squak { print "Eeeeeeeeeeek!\n"; } package Parrot::African; use base 'Parrot'; sub squak { print "EEEEEEEEEEEEEEEEEEEEEEEEK!\n"; } package Parrot::Pining; use base 'Parrot'; sub perch { my $this = shift; return SUPER::perch(@_) if $this->{at_fjords}; return undef; } sub squak { my $this = shift; return SUPER::squak(@_) if $this->{at_fjords}; return undef; }A call to squak() in a parrot is a notification that it should squak, or a request that it sqauk, never a garantee that a squak will be emitted.
# example of a switch style arrangement: sub doCommand { my $me = shift; my $cmd = shift; $cmd->isa('BleahCommand') or die; my $instr = $cmd->getInstructionCode(); if($instr eq 'PUT') { # PUT logic here } elsif($instr eq 'GET') { # GET logic here } # etc } # example of a variable method call arrangement: sub doCommand { my $me = shift; my $cmd = shift; $cmd->isa('BleahCommand') or die; my $instr = $cmd->getInstructionCode(); my $func = "process_" . $instr; return undef unless defined &$func; return $func->($cmd, @_); } # example of a variable subclass arrangement. # this assumes that %commandHandlers is set up with a list of object references. sub doCommand { my $me = shift; my $cmd = shift; $cmd->isa('BleahCommand') or die; my $insr = $cmd->getInstructionCode(); my $objectRef = $commandHandlers{$instr}; return $objectRef ? $objectRef->handleCommand($cmd, @_) : undef; }Since Perl offers AUTOLOAD, this idea could be emulated. If a package wanted to process an arbitrary and growing collection of commands to the best of its ability, it could catch all undefined method calls using AUTOLOAD, and then attempt to dispatch them (this assumes %commandHandlers is set up with a list of object references keyed by method name):
sub AUTOLOAD { my $me = shift; (my $methodName) = $AUTOLOAD m/.*::(\w+)$/; return if $methodName eq 'DESTROY'; my $objectRef = $commandHandlers{$methodName}; return $objectRef ? $objectRef->handleCommand($methodName, @_) : undef; }This converts calls to different methods in the current object to calls to a handleCommand() method is different objects. This is an example of using Perl to shoehorn a Command Object pattern onto a non Command Object interface.
package Iterator; sub hasNext { die; } sub getNext { die; }Other packages can come along and add Iterator to their @ISA list. They will need to redefine these methods. Now we have a uniform way of doing something. If a method in an object is expecting an Iterator as its argument, it has a way of checking to see if its argument really is an Iterator. It can be an Iterator and anything, else, too. This supports Type Safety.
package SampleTree; sub new { my $type = shift; my $this = { @_ }; bless $this, $type; } sub getIterator { my $this = shift; return new Foo::Iterator node=>$this; } sub get_left { my $this = shift; return $this->{'leftNode'}; } sub get_right { my $this = shift; return $this->{'rightNode'}; } package SampleTree::Iterator; sub new { my $type = shift; my %opts = @_; my $this = {state=>0, iterator=>undef, node=>$opts{'node'}; bless $this, $type; } sub getNext { my $this = shift; my $result; if($this->{'iterator'}) { $result = $this->{'iterator'}->getNext(); } if($result) { return $result; } elsif($this->{'state'} == 0) { # try the left node $this->{'iterator'} = $this->{'node'}->get_left(); $this->{'state'} = 1; return $this->getNext(); } elsif($this->{'state'} == 1) { # try the right node $this->{'state'} = 2; $this->{'iterator'} = $this->{'node'}->get_right(); return $this->getNext(); } else { # state == 2 return undef; } }This [17] code allows a network of objects having the getIterator method to cooperatively and transparently work together. Each object in the network may have a different way of iterating. This example represents a tree datastructure. The tree may contain other tree nodes, array objects, queues, and so forth. As long the network consists of objects with a getIterator() method that returns an object that implements the Iterator iterface, we can crawl through the whole thing. Thats composition you can take to the bank and smoke!
# slurp everything into memory, then work on it: open my $file, 'dataset.cvs' or die $!; read $file, my $data, -s $file or die $!; close $file; foreach my $i (split /\n/, $data) { # process } # process as we read: my $process = sub { # process }; open my $file, 'dataset.cvs' or die $!; while(my $record = <$file>) { $process->($record); } close $file;Returning all of the data from a get_ method fosters slurping everything into memory. This fosters programers which are limited by memory in how large of datasets they can work on. You can chuckle and say that virtual memory will take up the slack, but if I can tell you that there are a heck of a lot of multi terrabyte data warehouses kicking around the world. Dealing with data in place, where your storage is essentially at capacity at all times, or having multiple clients process a very large dataset in parallel demands efficiency. There are still a few applications for good programmers and a few applications for good programmers to write.
package RecordReader; use ImplicitThis; @ISA = qw(Interface); sub new { my $type = shift; my $file = shift; open my $filehandle, $file or die $!; my $me = { handle => $filehandle, next => undef }; bless $me, $type; } sub getNext { return $next if defined $next; return <$handle>; } sub hasNext { return 1 if defined $next; $next = <$me>; if($next) { return 1; } else { close $fh; return 0; } }Compare this to Java's IO Filters, which will superimpose read of datastructures, international characters, and so forth on top of IO strems: you'll find it to be remarkably similar. It lets users mix and match IO processing facilities.
# note: no package statement use DBI; use CGI; use Mail::Sendmail;Back in the main program:
use config; my $userid = CGI::param('userid'); # etc...my variables are file-global when declared outside of any code blocks, which means that we can't easily declare lexical variables in config.pm and have them show up in the main program. We can co-opt the import() method of config.pm to create local variables in the main program, though:
# back in config.pm: my %config = ( maxusers => 100, retriespersecond => 2, loglevel => 5 ); sub import { my $caller = caller; foreach my $i (keys %config) { local ${$caller.'::'.$i}; *{$caller.'::'.$i} = $config{$i}; } }This will atleast squelsh any warnings Perl would otherwise emit and let us return to importing configuration dependent values from a configuration file.
package Preferences; sub new { my $class = shift; my %args = @_; bless {color=>$args{'color'}, petname=>$args{'petname'}, street=>{'street'} }, $class; } sub query_color { return $_[0]->{'color'}; } sub set_color { return $_[0]->{'color'} = $_[1]; } # other accessors here 1; package main; $| = 1; print "Whats your favorite color? "; my $color = <STDIN>; print "Whats your pets name? "; my $petname = <STDIN>; print "What street did you grow up on? "; my $street = <STDIN>; my $foo = new Preferences (color=>$color, petname=>$petname, street=>$street);The string "color" appears ten times. Ten! In Perl, no less. If I wrote out the constructors for the other arguments, this would be repeated for each variable. Shame. If we trust the user to pass in the right things to the constructor, we can get rid of two. Still, even typing each thing eight times is begging for a typo to come rain on your parade.
package main; $| = 1; sub get_preferences { print "Whats your favorite color? "; my $color = <STDIN>; print "Whats your pets name? "; my $petname = <STDIN>; print "What street did you grow up on? "; my $street = <STDIN>; return MessageMethod sub { my $arg = shift; ({ query_color => sub { return $color; } set_color => sub { $color = shift; return 1; } # etc }->{$arg} || sub { die "Unknown request: $arg" })->(@_); }; } my $ob = get_preferences(); print $ob->get_street(), "\n";First, the { query_name => sub { } }->{$arg}->(@_) is a sort of switch/case statement. It creates an anonymous hash of names to functions, then looks up one of the functions by name, using the first argument passed in. Once we have that code reference, we execute it and pass it our unused arguments. Then we've added a default case to it, so we don't try to execute undef as code. This could have been coded using if/elsif/else just as easily.
package MessageMethod; sub new { my $type = shift; return $type->new(@_) if ref $type eq PACKAGE__; my $ref = shift; ref $ref eq 'CODE' or die; bless $ref, $type; } sub AUTOLOAD { my $me = shift; (my $method) = $AUTOLOAD =~ m/::(.*)$/; return undef if $method eq 'DESTROY'; return wantarray ? ($me->($method, @_)) : scalar $me->($method, @_); } 1;Given a code reference, MessageMethod blesses it into its own package. There are no methods aside from new() and AUTOLOAD(). AUTOLOAD handles undefined methods for Perl, and since there are no methods, it handles all of them. (There is an exception to that, where new() has to pass off requests). AUTOLOAD() merely takes the name of the function it is standing in for and sends that as the first argument to a call to the code reference, along with the rest of the arguments. We're translating $ob->foo('bar') into $ob->('foo', 'bar'). This does nothing but let us decorate our code reference with a nice OO style syntax.
# place this code in hashclosure.pm # tell Perl how to find methods in this object - run the lambda closures the object contains sub AUTOLOAD { (my $method) = $AUTOLOAD =~ m/::(.*)$/; return if $method eq 'DESTROY'; our $this = shift; if(! exists $this->{$method}) { my $super = "SUPER::$method"; return $this->$super(@_); } $this->{$method}->(@_); } 1;This code translates method calls into invocations of anonymous subroutines by the same name inside of a blessed hash: when a method is called, we look for a hash element of that name, and if we find it, we execute it as a code reference.
package Foo; sub new { my $class = shift; my %args = @_; our $this; my $foo; my $bar; bless { get_foo => sub { return $foo }, set_foo => sub { $foo = shift }, get_bar => sub { return $bar }, set_bar => sub { $bar = shift }, get_foo_bar_qux => sub { return $this->get_foo(), $this->get_bar(), get_qux(); }, dump_args => sub { foreach my $i (keys %args) { print $i, '=', $args{$i}, "\n"; } }, }, $class; } sub get_qux { return 300; }This blesses an anonymous hash reference into our package, Foo. This hash reference contains method names as keys and anonymous subroutines as values. AUTOLOAD() knows how to look into our hash and find methods by name, and run them, rather than looking for methods in the normal place.
package Constrain; # component - anonymous functions that exert force on each other. # these are generated by various functions, much as an # object in OO Perl would be created. sub new { my $type = shift; my $subtype = shift; return new Constrain::Adder(@_) if $subtype eq 'adder'; return new Constrain::Constant(@_) if $subtype eq 'constant'; return new Constrain::Probe(@_) if $subtype eq 'prober'; return new Constrain::Connector(@_) if $subtype eq 'connector'; warn "Unknown Constrain subtype: $subtype"; } package Constrain::Adder; sub new { my $type = shift; my $a1 = shift; # the name of our first connector my $a2 = shift; # the name of 2nd connector we are tied to my $sum = shift; # the name of 3rd connector we are tied to my $obj = { a1=>$a1, a2=>$a2, sum=>$sum }; bless $obj, $type; $a1->xconnect($obj); $a2->xconnect($obj); $sum->xconnect($obj); return $obj; } sub forgetvalue { my $this = shift; $a1->forgetvalue($obj); $a2->forgetvalue($obj); $sum->forgetvalue($obj); $this->set_value(undef); } sub setvalue { my $this = shift; local *a1 = \$this->{a1}; local *a2 = \$this->{a2}; local *sum = \$this->{sum}; if($a1->hasvalue() and $a2->hasvalue()) { $sum->setvalue($a1->getvalue() + $a2->getvalue(), $this); } elsif($a1->hasvalue() and $sum->hasvalue()) { $a2->setvalue($sum->getvalue($sum) - $a1->getvalue($a1), $this); } elsif($a2->hasvalue() and $sum->hasvalue()) { $a1->setvalue($sum->getvalue() - $a2->getvalue(), $this); } } sub dump { my $this = shift; local *a1 = \$this->{a1}; local *a2 = \$this->{a2}; local *sum = \$this->{sum}; print("a1 has a value: ", $a1->getvalue(), "\n") if $a1->hasvalue(); print("a2 has a value: ", $a2->getvalue(), "\n") if $a2->hasvalue(); print("sum has a value: ", $sum->getvalue(), "\n") if $sum->hasvalue(); } package Constrain::Constant; sub new { my $type = shift; my $value = shift; # our value. we feed this to anyone who asks. my $connector = shift; # who we connect to. my $obj = { value => $value, connector => $connector }; bless $obj, $type; $connector->xconnect($obj); $connector->setvalue($value, $obj); return $obj; } sub setvalue { my $this = shift; my $value = shift; $this->{connector}->setvalue($value, $this); } sub getvalue { my $this = shift; return $this->{value}; } package Constrain::Probe; sub new { my $type = shift; my $connector = shift; my $name = shift; my $obj = { connector => $connector, name => $name }; bless $obj, $type; $connector->xconnect($obj); return $obj; } sub setvalue { my $this = shift; my $name = $this->{name}; print "Probe $name: new value: ", $this->{connector}->getvalue(), "\n"; } sub forgetvalue { my $this = shift; my $name = $this->{name}; print "Probe $name: forgot value\n"; } package Constrain::Connector; sub new { my $type = shift; my $obj = { informant=>undef, value=>undef, dontreenter=>0, constraints=>[] }; bless $obj, $type; } sub hasvalue { my $this = shift; return $this->{informant}; } sub getvalue { my $this = shift; return $this->{value}; } sub setvalue { my $this = shift; local *constraints = \$this->{constraints}; my $newval = shift; my $setter = shift or die; return if $this->{dontreenter}; $this->{dontreenter} = 1; $this->{informant} = $setter; $this->{value} = $newval; foreach my $i (@$constraints) { $i->setvalue($newval, $this) unless $i eq $setter; } $this->{dontreenter} = 0; } sub forgetvalue { my $this = shift; local *constraints = \$this->{constraints}; my $retractor = shift; if($this->{informant} eq $retractor) { $this->{informant} = undef; foreach my $i (@$constraints) { $i->forgetvalue($this) unless $i eq $retractor; } } } sub xconnect { my $this = shift; local *constraints = \$this->{constraints}; local *value = \$this->{value}; my $newconstraint = shift or die; push @$constraints, $newconstraint; $newconstraint->setvalue($value, $obj) if $value; } package main; my $a = Constrain::Connector->new(); my $a_probe = Constrain::Probe->new($a, 'a_probe'); my $b = Constrain::Connector->new(); my $b_probe = Constrain::Probe->new($b, 'b_probe'); my $c = Constrain::Connector->new(); my $c_probe = Constrain::Probe->new($c, 'c_probe'); my $a_b_adder = Constrain::Adder->new($a, $b, $c); my $a_const = Constrain::Constant->new(128, $a); my $b_const = Constrain::Constant->new(256, $b);XXX - constraint system example - IK system using X11::Protocol?
package DBI::Record; my $foreign_keys = {}; sub import { # read foreign key information # translates a foreign column name to a table to its table # $foreign_keys{'FooID'} = 'Foo'; while(my $i = shift) { $foreign_keys{$i} = shift; } } sub new { my $type = shift; $type = ref $type if ref $type; my $me = { }; my $usage = 'usage: new DBI::Record $dbh, $sql | ($sth, $sth->fetchrow_hashref())'; my $dbh = shift; ref $dbh or die $usage; my $rs = shift; my $sth; my $sql; die $usage unless @_; if(ref $_[0]) { $sth = shift; $rs = shift or $rs = $sth->fetchrow_hashref(); } else { $sql = shift; $sth = $dbh->prepare($sql); $sth->execute(); $rs = $sth->fetchrow_hashref(); } $me->{'database_handle'} = $dbh; $me->{'record_set'} = $rs; $me->{'statement_handle'} = $sth; # generate accessors foreach my $i (keys %$rs) { *{$i} = sub { my $me = shift; my $sth = $dbh->prepare("select * from $foreign_keys{$i} where $i = $rs->{$i}"); $sth->execute(); my $newrs = $sth->fetchrow_hashref; return $me->new($dbh, $newrs, $sth); } } bless $me, $type; } sub next { my $me = shift; my $sth = $me->{'statement_handle'} or return undef; my $newrs = $sth->fetchrow_hashref() or return undef; return $me->new($me->{'database_handle'}, $sth, $newrs); } package main; use DBI::Record CustomerID => Customers, BillID => Bills; use DBI; my $dbh = DBI->connect("DBI:Pg:dbname=geekpac", 'ingres', '') or die $dbh->errstr; my $customer = new DBI::Record $dbh, "select * from Users limit 1"; my $bill = $customer->BillID(); while($bill) { print $bill->{'BillID'}, " ", $bill->{'Amount'}, "\n"; $bill = $bill->next(); }This makes it easy to navigate relationships in a relational database system, but doesn't do a lot for us in the way of reporting.
select self1 as foo, self2 as bar from self as self1, self as self2 where self1.name = self2.paramOr something like:
foreach my $i (keys %hash) { if(exists $hash{$i} and exists $hash{$hash{$i}}) { push @results, [$i, $hash{i}, $hash{$hash{$i}}]; } }Ugly, slow, crude, effective. People have been known to write code generators and SQL generators when faced with degenerate cases like these that automate ugliness production. I guess you could categories this as an AntiPattern in the form of a CodeSmell.
select count(*) as isDongle from Product, Category, ProductToCategory where Product.ProductID = ProductToCategory.ProductID and ProductToCategory.CategoryID = Category.CategoryID and Category.Name = 'Dongle'This query returns the number of dongles in the database. Replacing count(*) with a specific field list would return details of each dongle.
my $output = new Output; my $backend = new Backend($output); $output->set_backend($backend);Or:
my $output = new Output($this);Refactor as a:
my $output = new Output; my $backend = new Backend($output->get_backend_adapter()); $output->set_backend($backend->get_output_adapter());Or...
my $output = new Output($this->get_output_adapter());ModelViewController
sub foo { my %args = @_; my $color = $args{color}; my $number = $args{number}; # ... } foo(color=>'red', number=>13); The || operator lets you easily provide defaults and error checking: sub foo { my %args = @_; my $color = $args{color} || 'red'; my $number = $args{number} || die 'number=> paramer required'; # ... } Or, you may explicitly list the argument names and defaults, providing a self-documenting framework: sub foo { my %args = ( Arg1 => 'DefaultValue', Blah => 42, Bleh => 60*60*24, Hostname => undef, @_ ); # Handle error-checking here defined $args{Hostname} or die 'Required parameter "Hostname" not defined'; }See Also
my $context = { increment => sub { my $context = shift; $context->{sum}++; return ''; }, currentvalue => sub { my $context = shift; return $context->{sum}; } }; sub expand_macros { my $context = shift; my $text = shift; my $macro = qr{([A-Z][A-Z0-9]{2,})}; $text =~ s/$macro/$context->{lc($1)}->($context)/ge; return $text; } expand_macros($context, "INCREMENT INCREMENT The current value is: CURRENTVALUE");This is fairly strightfoward: We can pass $context and some text containing the macros "INCREMENT" and "CURRENTVALUE" to expand_macros(), and the macros will increment the current value of $context->{sum} and return the value. This is a simple template parser that finds escapes in text and replaces them with the result of a peice of code passed in through a hash. However, since we're maintaing our context in a hash reference, we can do this recursively:
$context->{doubleincrement} = sub { my $context = shift; expand_macros($context, "INCREMENT INCREMENT CURRENTVALUE"); } expand_macros($context, "The current value is: DOUBLEINCREMENT");Maintaining state in a hashref rather than the symbol table only requires us to be vigilent in passing the hash ref around. We have access to the updated state in the hashref after evaluation has finished. We can take this same context and pass it off again. In our example, we could template something else, reusing our same state and collection of macro definitions.
# defining our mini language: # format of our macro escapes. returns the name of the macro. $macro = qr{([A-Z][A-Z0-9]{2,})}; sub fetchvalue() { my $symbol = lc(shift()); my $ob = shift; return $ob->{$symbol} if defined $ob->{$symbol}; return $symbol->($ob) if defined &{$symbol}; # if its available as a function, recurse into it return $$symbol; # assume its a scalar } sub createtemplate { my $name = shift; my $text = shift; *{$name} = sub { my $ob = shift; my $text = $text; # private copy, so we don't ruin the original $text =~ s{$macro}{ fetchvalue($1, $ob); }oges; return $text; }; } sub createquery { my $name = shift; # name of function to create my $sql = shift; # query this function will execute my $inner = shift; # name of function to call with each result, optional my @queryargs; $sql =~ s{('?)$macro\1}{push @queryargs, lc($2);'?'}oges; my $sth = $dbh->prepare($sql, @queryargs); *{$name} = sub { my $ob = shift; my $row; my $ret; $sth->execute(map { fetchvalue($1, $ob); } @args); my @names = @{$sth->{'NAME'}}; while($row = $sth->fetchrow_arrayref()) { # store each item by its column name for(my $i=0;$i < @names; $i++) { $ob->{$names[$i]} = $row->[$i]; } # if we're supposed to send each record to someone, do so. $ret .= $inner->($ob) if($inner); } $sth->finish(); return $ret; }; } # writing code in our mini language: createquery('readnames', qq{ select Name as name from Users where Name is not null }); createquery('readnumberbyageinstate', qq{ select count(*) as number, Age as agearoup from Users where State = STATE group by Age }, 'drawbargraph'); createtemplate('drawbargraph', qq{ <div align="left"><img src="reddot.png" height="20" width="NUMBER"></div> }); print readnames(); print readnumberbyageinstate({state=>'MD'});Lets take a look at what we've factored out in this example:
print createquery($readnumberbystatesql, {drawpiechart => createpiechart() }, 'drawpiechart');It is traditional in languages like Lisp and Scheme to skip naming things unless actually necessary.
eval { run_query(); }; if($@) { $dbh = DBI->connect("DBI:Pg:dbname=blog;host=localhost;port=5432", 'scott', 'foo'); run_query(); }See also: TypeSafety, TypedVariables, AccessorPattern, BigBallOfMud, ObjectOriented, DaemonPattern, ErrorReporting
my $lock; sub notify_all { if($lock) { warn "Don't respond to an event with an event!"; $lock++; } foreach my $listener (@listeners) { $listener->send_event(@_); } $lock = 0; }In most cases, it is never an error to be called back by the object that you just called. Some times re-entry isn't an error at all, and you can silently refuse it. ConstraintSystem uses this idea to propogate values across a network where some nodes are willing to budge and others aren't. Usually this manifests as a list of notification recipients that receive a notification, and one needs to send yet another notice to all of them except the sender of the original message, but doesn't happen to know which originated. This situation crops up with the Gnutella protocol, where nodes replay messages to every peer except the originating one, but the mesh of connections can cause the message to be accidentally routed to the originator anyway. Simpily tracking which messages you originated yours and ignoring requests to forward them again pervents a condition where a host transmits the same message out onto the net over and over.
sub notify_all { if($testing) { # never do this in production code! my $calldepth = 0; $callerdepth++ while(caller($calldepth)); die "arbitrary limit exceeded: stack depth too deep, possible runaway recursion detected" if $callerdepth > 100; } foreach my $listener (@listeners) { $listener->send_event(@_); } }See Also: ConstraintSystem, EventListeners
my $shbit = 1 << fileno($sh); my $sibit = 1 << fileno($si); my $inbitmask = $shbit | $sibit; # select(readtest, writetest, exceptiontest, max wait) select($inbitmask, undef, undef, 0); if($inbitmask & $shbit) { # $sh is ready for read } if($inbitmask & $sibit) { # $si is ready for read }Done in a loop, several sources of input - perhaps the network, a GUI interface, pipes connected to other processes - could all be managed. The last argument to select() is typically 0 or undef, though it is sometimes other numbers. If it is undef, select() will wait indefinately for input. If it is 0, select() will return immediately, input ready or not. Any other number is a number of seconds to wait, floating point numbers accepted. As soon as a any monitored input or output handle becomes ready, select() will return. select() doesn't return a value in the normal sense: it motifies the bit mask, turning off any bits that correspond to fileno() bit positions that aren't ready. Each bit that we set must be tested to see if it is still on. If it is, that filehandle is ready for read or write. Filehandles that we want to monitor for read are passed as a bitmask in the first argument position of select(). The second argument of select() is the filehandles to monitor for write, and the third, for exceptions.
if($inbitmask & $sibit) { $si->process_input(); }Filehandles may be blessed into classes [25], and then methods called to handle the event where input becomes available for read. This is easy to implement, simple, and sane - to implement. Using it is another story.
package IO::Network::GnutellaConnection; use base 'IO::Handle'; sub process_input() { my $self = shift; $self->read(...); }Each access must promptly return for other handles to be served. This is a big requirement. Unheaded, a user interface could repeatedly cause network traffic to time out, or one unresponsive process reading on a pipe to lock up the process writing on the pipe - see PerlGotchas for more. These cases are more numerous and insideous than thread CPU starvation issues.
package Xfor; sub new { my $pack = shift; my $filecache; # holds all of the name->value pairs for each item in each file my $buffered; # same format: data to write to file yet bless { # open a flatfile database. create it if needed. open => sub { my $fn = $_[0]; unless(-f $fn) { open F, '>>'.$fn or return 0; close F; } $self->openorfail($fn); }, # open a flatfile database. fail if we are unable to open an existing file. openorfail => sub { my $file = shift; # which file the data is in open my $f, $file or die $!; my $k; my $v; while(<$f>) { chomp; %thingy = split /\||\=/, 'key='.$_; while(($k, $v) = each %thingy) { $filecache->{$file}->{$thingy{'key'}}->{$k} = $v; } } close $f; return 1; }, # fetch a value for a given key get => sub { my $file = shift; # which file the data is in my $thingy = shift; # which record in the file - row's primary key my $xyzzy = shift; # which column in that record $logic->openflatfile($file) unless(exists $filecache->{$file}); return $filecache->{$file}->{$thingy}->{$xyzzy}; }, keys => sub { my $rec = $filecache; while(@_) { $rec = $rec->{$_[0]}; shift; } if(wantarray) { keys %{$rec}; } else { $rec; } }, set => sub { my $file = shift; # which file the data is in my $thingy = shift; # which record in the file - row's primary key my $x = shift; # which column in that record my $val = shift; # new value to store there $filecache->{$file}->{$thingy}->{$x} = $val; $buffered->{$file}->{$thingy}->{$x} = $val; 1; }, close => sub { my $file = shift; # which file the data is in my $thingy; # which record in the file - row's primary key my $x; # which column in that record my $val; # new value to store there my $line; # one line of output to the file open my $f, '>>'.$file or die "$! file: $file"; foreach $thingy (keys %{$buffered->{$file}}) { $line = $thingy; foreach $x (keys %{$buffered->{$file}->{$thingy}}) { $line .= '|' . $x . '=' . $buffered->{$file}->{$thingy}->{$x}; } print F $line, "\n"; } $buffered->{$file} = (); close $f; }, recreate => sub { my $file = shift; # which file the data is in my $thingy; # which record in the file - row's primary key my $x; # which column in that record my $val; # new value to store there my $line; # one line of output to the file open my $f, ">$file.$$" or die "$! file: $file.$$"; foreach $thingy (keys %{$filecache->{$file}}) { $line = $thingy; foreach $x (keys %{$filecache->{$file}->{$thingy}}) { $line .= '|' . $x . '=' . $filecache->{$file}->{$thingy}->{$x}; } print $f $line, "\n"; } close F; rename "$file.$$", $file or die "$! on rename $file.$$ to $file"; }, } , $pack; }To use, do something like:
use Xfor; my $hash = new Xfor; $hash->open('carparts.nvp'); # read: $hash->get('carparts.nvp', 'xj-11', 'muffler'); # which muffler does the xj-11 use? # write: $hash->set('cartparts.nvp', 'xj-11', 'muffler', 'c3p0'); # then later: $hash->close('carparts.nvp'); # or... $hash->recreate('carparts.nvp');Xfor.pm reads files from beginning to end, and goes with the last value discovered. This lets us write by kind-of journeling: we can just tack updated information on to the end. we can also regenerate the file with only the latest data, upon request. Since we read in all data, we're none too speedy. Reading is as slow as Storable or the like, but writing is much faster.
# go out of our way to include sid=$sid: print qq{<a href="otherprog.cgi?foo=bar&color=red&sid=$sid">Go To Otherprog</a>}; print qq{ <form action="anotherprog.cgi" method="post"> <input type="hidden" name="sid" value="$sid"> Enter answer: <input type="text" name="answer"><br> <input type="submit"> </form> };Forgetting to do this in even one link or form causes the site to forget any and all information about a user as soon as they click it. Additionally, since the sessionid is part of the HTML, it lives on in the browser cache. For this reason, session id tokens should be expired after a period of time by the server. This means having the server simply record the date that it issued a session id number and refusing to honor it after a period of time has elapsed, forcing the user to re-login.
$oOo =~ s/<(a|frame)([^>]*) (src|href)=(['"]?)(?!javascript)([^'"]+\.htm)(l)?(\?)?([^'">]*)?\4(?=\w|>>)/<$1$2 $3="$5$6\?$8\&sid=$sid"/ig; # $1: 'a' or 'frame' # $2: any random name=value pairs (exa 'name="mainFrame"') # $3: 'src' or 'href' # $4: any begin qouting character, be it ' or " # $5: whatever.htm # $6: optional 'l' # $7: optional '?' (discarded) # $8: optional cgi get string # $9: 0-width lookahead assertion: > or space isn't matched but is looked for
# Sample validateuser.pm: use CGI; use CGI::Carp qw/fatalsToBrowser/; use DBI; use lib "/home/scott/cgi-bin/DBD"; BEGIN { $dbh = DBI->connect("DBI:Pg:dbname=sexcantwait;host=localhost;port=5432", 'scott', 'pass') or die $DBI::errstr; } use TransientBaby::Postgres; use TransientBaby; createquery('validateuser', qq{ select UserID as userid from Users where Name = [:username:] and Pass = [:userpass:] }); sub validated { $userid = -1; my $sid=CGI::cookie(-name=>"sid"); return 0 unless $sid; ($username, $userpass) = split /,/, $sid; validateuser(); return $userid == -1 ? 0 : 1; } sub is_guest { return $username =~ /^guest/; } sub offer_login { print qq{ Sorry, you aren't logged in. Please enter your name and password:<br><br> <form action="login.cgi" method="post"> <input type="hidden" name="action" value="login"> User name: <input type="text" name="username"><br> Password: <input type="password" name="password"><br> Are you a new user? <input type="checkbox" name="newuser"><br> <input type="submit" value="Log in"><br> </form> }; exit 0; } 1;Instead of declaring a package and using Exporter, we're merely continuing to operate in the namespace of the module that invoked us. The methods we define - validated(), validateuser(), offer_login() and is_guest() show up in their package, ready for use. As a side effect, we're using CGI.pm and DBI.pm on behalf of our caller, letting us list all of the modules we want in only one place, rather than in every .cgi script. This module could be used with:
print qq{Content-type: text/html\n\n}; use validateuser; validated() or offer_login(); # rest of the script here, for users onlyoffer_login() never returns once we call it. It handles exiting the script for us.
#!/usr/bin/perl # example login/create user script that uses validateuser.pm. # this should be named login.cgi to match the form in validateuser.pm, unless of course # that form's action is changed. use validateuser; createquery('userexists', qq{ select count(*) as num from Users where Users.Name = [:name:] }); createquery('createuser', qq{ insert into Users (Name, Pass, CreationIP) values ([:name:], [:pass:], [:creationip:]) }); my $action = CGI::param('action'); my $newuser = CGI::param('newuser'); if(!$action) { offer_login(); } elsif($action eq 'login' and !$newuser) { $username = CGI::param("username"); $userpass = CGI::param("userpass"); validateuser(); if($userid != -1) { my $cookie=CGI::cookie( -name=>'sid', -expires=>'+18h', -value=>qq{$username,$userpass}, -path=>'/', -domain=>'.sexcantwait.com' ); print CGI::header(-type=>'text/html', -cookie=>$cookie); print qq{Login successful.\n}; } else { sleep 1; # frustrate brute-force password guessing attacks print qq{Content-type: text/html\n\n}; print qq{Login failed! Please try again.<br>\n}; offer_login(); } } elsif($newuser and $action eq 'login') { local $name = CGI::param("username"); local $pass = CGI::param("userpass"); userexists(); if($num) { print qq{User already exists. Please try again.<br>\n}; offer_login(); } local $creationip = $ENV{REMOTE_ADDR}; createuser(); validateuser(); # sets $userid print qq{Creation successful! Click on "account" above to review your account.<br>\n}; }These examples make heavy use of my TransientBaby.pm module. That module creates recursive routines that communicate using global variables - ick. I need to change that, and then this example. Then I'll put that code up. XXX.
use TransientBaby::Forms; use TransientBaby; my $accessor; my %opts; my @table; my $tablerow; my $tablecol = -1; parse_html($document, sub { $accessor = shift; %opts = @_; if($opts{tag} eq 'tr') { # create a new, blank array entry on the end of @table $tablerow++; $table[$tablerow] = []; $tablecol = 0; } elsif($opts{tag} eq 'td') { # store the text following the <td> tag in $table[][] $table[$tablerow][$tablecol] = $accessor->('trailing'); $tablecol++; } });I've gone out of my way to avoid the nasty push @{$table[-1]} construct as I don't feel like looking at it right now. $tablerow and $tablecol could be avoided otherwise. This code watches for HTML table tags and uses those to build a 2 dimentional array.
select table1.a, table2.b, table3.c from table1, table2, table3 where table1.id = table2.id and table2.param = table3.id order by table1.a, table2.b, table3.cWe can't recover the id or param fields from the output of this query, but we can generate our own.
aaa aab aac aad aba aca ada baa bab (And so on...)Add this clause to the if statement in the sub passed to parse_html() above, remembering to declare the introduced variables in the correct scope:
} elsif($opts{tag} eq '/tr') { if(!$tablerow or $table[$tablerow][0] ne $table[$tablerow-1][0]) { $dbh->execute("insert into tablea (a) values (?)", $table[$tablerow][0]); $table_a_id = $dbh->insert_id(); # else $table_a_id will retain its value from the last pass } if(!$tablerow or $table[$tablerow][1] ne $table[$tablerow-1][1]) { $dbh->execute("insert into tableb (b, id) values (?, ?)", $table[$tablerow][1], $table_a_id); $table_b_id = $dbh->insert_id(); # else $table_b_id will retain its value from the last pass } if(!$tablerow or $table[$tablerow][2] ne $table[$tablerow-1][2]) { $dbh->execute("insert into tablec (c) values (?, ?)", $table[$tablerow][1], $table_b_id); $table_c_id = $dbh->insert_id(); # else $table_c_id will retain its value from the last pass } }This code depends on $dbh being a properly initialized database connection. I'm using ->insert_id(), a MySQL extention, for clarity. Unlike the previous code, this code is data-source specific. Only a human looking at the data can deturmine how best to break the single table up into normalized, relational tables. We're assuming three tables, each having one column, aside from the id field. Assuming this counting pattern, we insert records into tablec most often, linking them to the most recently inserted tableb record. tableb is inserted into less frequently, and when it is, the record refers to the most recently inserted record in tablea. When a record is inserted into tablea, it isn't linked to any other records.
{ local $/ = undef; open FH, "<$file"; $data = <FH>; close FH; }Pros: Everyone seems to know this one. Reads in entire file in one gulp without an array intermediary. Cons: $data cannot be declared with my because we have to create a block to localize the record seperator in. Ugly.
@ARGV = ($file); my $data = join '', <>;Pros: Short. Sweet. Cons: Clobbers @ARGV, poor error handling, inefficient for large files.
my $data = `cat $file`;Pros: Very short. Makes sense to sh programmers. Cons: Secure problem - shell commands may be buried in filenames. Creates an additional process - poor performance for files small and large. No error handling. Is not portable.
open my $fh, '<', $file or die $!; read $fh, my $data, -s $fh or die $!; close $fh;Pros: Good error handling. Reasonably short. Efficient. Doesn't misuse Perl-isms to save space. Uses lexical scoping for everything. Cons: None.
use Sys::Mmap; new Mmap my $data, -s $file, $file or die $!;Pros: Very fast random access for large files as sectors of the file aren't read into memory until actually referenced. Changes to the variable propogate back to the file making read/write, well, cool. Cons: Requires use of an external module such as Sys::Mmap, file cannot easily be grown. Difficult for non-Unix-C programmers to understand.
require 'config.pl';We've all seen it a million times. It's as old as Perl itself. You make a little Perl program that does nothing but assign values to variables. Users can "easily" edit the file to change the values without having to wade through your source code. It is extremely easy to read configuration files of this format, and you can store complex datastructures in there, along with comments.
# config.pl: $config = { widgets=>'max', gronkulator=>'on', magic=>'more' }; # configTest.pl: use Data::Dumper; require 'config.pl'; $config->{gronkulator} = 'no, thanks'; open my $conf, '>config.pl' or die $!; print $conf Data::Dumper->Dump($config); close $conf;Data::Dumper.pm comes with Perl, and can even store entire objects. In fact, it can store networks of object.
# don't do this sub barf { print "something went wrong!\n", @_; exit 1; } # ... barf("number too large") if($number > $too_large);die() has a useful default behavior that depends on no external modules, but can easily be overriden with a handler to do more complex cleanup, reporting, and so on. If you don't use die(), you can't easily localize which handler is used in a given scope.
# intercept death long enough to scream bloody murder $version = '$Id: ErrorReporting,v 1.7 2003/03/04 11:51:24 phaedrus Exp $'; # CVS will populate this if you use CVS $SIG{qq{__DIE__}} = sub { local $SIG{qq{__DIE__}}; # next die() will be fatal my $err = ''; $err .= "$0 version $version\n\n"; # stack backtrace $err .= join "\n", @_, join '', map { (caller($_))[0] ? sprintf("%s at line %d\n", (caller($_))[1,2]) : ''; } (1..30); $err.="\n"; # report on the state of global variables. this includes 'local' variables # and 'our' variables in scope. see PadWalker for an example of inspecting # lexical 'my' variables as well. foreach my $name (sort keys %{__PACKAGE__.'::'}) { my $value = ${__PACKAGE__.'::'.$name}; if($value and $name ne 'pass' and $name =~ m/^[a-z][a-z0-9_]+$/) { $err .= $name . ' ' . $value . "\n" } } $err .= "\n"; foreach my $name (sort keys %ENV) { $err .= $name . ' ' . $ENV{$name} . "\n"; } $err .= "\n"; # open the module/program that triggered the error, find the line # that caused the error, and report that. if(open my $f, (caller(1))[1]) { my $deathlinenum = (caller(1))[2]; my $deathline; # keep eof() from complaining: <$f>; $deathline = <$f> while($. != $deathlinenum and !eof); $err .= "line $deathline reads: $deathline\n"; close <$f>; } # send an email off explaining the problem # in text mode, errors go to the screen rather than by email require Mail::Sendmail; sendmail(To=>$errorsto, From=>$mailfrom, Subject=>"error", Message=>$err) unless($test); print "<pre>\n", CGI::escapeHTML($err), "</pre>\n" if($test); # reguardless, give the user a way out. in this case, we display what was in their # shopping cart and give them a manual order form that just sends an email, and we # call them back for payment info. $|=1; # print "Those responsible for sacking the people that have just been sacked, have just been sacked.<br><br>\n"; print "A software error has occured. Your order cannot be processed automatically. "; print "At the time of the error, your cart contained:<br><br>\n"; open my $cart, $cartdir.$sid; print "$_<br>\n" while(<$cart>); print qq{ <script language="javascript"> window.open("$errororderpage"); </script> }; close $cart; # finally, give up exit 0; };Die Early, Die Often
open my $f, 'file.txt' or die $!;or die should litterally dot your code. Thats how you communicate to Perl and your readership that it is not okey for the statement to silently fail. Most languages make such error geeration default; in Perl, you must request it. This is no excuse for allowing all errors to sneak by silently.
# from the Fatal.pm perldoc: use Fatal qw(open close); sub juggle { . . . } import Fatal 'juggle';Fatal.pm will place wrappers around your methods or Perl built in methods, changing their default behavior to throw an error. A module which does heavy file IO on a group of files need not check the return value of each and every open(), read(), write(), and close(). Only at key points - on method entry, entry into worker functions, etc - do you need to handle error conditions. This is a more reasonable request, one easily acheived. Should an error occur and be cought, the text of the error message will be in $@.
use Fatal qw(open close read write flock seek print); sub update_data_file { my $this = shift; my $data = shift; my $record; local *filename = \$this->{filename}; local *record = \$this->{record}; eval { open my $f, '>+', $filename; flock $f, 4; seek $f, $record, 0; print $f, $data; close $f; }; return 0 if $@; # update failed return 1; # success }Alternatively, rather than using eval { } ourselves, following AssertPattern, we could trust that someone at some point installed a __DIE__ handler. The most recently installed local handler gets to try to detangle the web.
sub generate_report { local $SIG{__DIE__} = { print "Whoops, report generation failed. Tell your boss it was my fault. Reason: ", @_; } foreach my $i ($this->get_all_data()) { $data->update_data_file($i); } } sub checkpoint_app { local $SIG{__DIE__} = { print "Whoops, checkpoint failed. Correct problem and save your data. Reason: ", @_; } $data->update_data_file($this->get_data()); }Using local scoped handlers this way allows you to provide context-sensitive recoverory, or atleast diagnostics, when errors are thrown. This is easy to do and all that is required to take full advantage of Fatal.pm.
Fatal.pm was written by Lionel.Cons@cern.ch with prototype updates by Ilya Zakharevich ilya@math.ohio-state.edu.
pacakge UserExtention1; # we are expected to have a "run_macro" method sub run_macro { my $this = shift; my $app = shift; $app->place_cursor(0, 0); $app->set_color('white'); $app->draw_circle(radius=>1); $app->set_color('red'); $app->draw_circle(radius=>2); # and so on... make a little bull's eye return 1; }The main application could prompt the user for a module to load, or load all of the modules in a plug-ins directory, then make them available as menu items in an "extentions" menu. When one of the extentions are select from the menu, a reference to the application - or a FacadePattern providing an interface to it - is passed to the run_macro() method of an instance of that package.
place_cursor(0, 0) set_color(white) draw_circle(radius=1) set_color(red) draw_circle(radius=2)A few options exist: we can compile this directly to Perl bytecode using B::Generate (suitable for integrating legacy languages without performance loss), or we can munge this into Perl and eval it. Lets turn it into Perl.
# read in the users program my $input = join '', <STDIN>; # 0 if we're expecting a function name, 1 if we're expecting an argument, # 2 if we're expecting a comma to seperate arguments my $state = 0; # perl code we're creating my $perl = ' package UserExtention1; sub run_macros { my $this = shift; my $app = shift; '; while(1) { # function call name if($state == 0 && $input =~ m{\G\s*(\w+)\s*\(}cgs) { $perl .= ' $app->' . $1 . '('; $state = 1; # a=b style parameter } elsif($state == 1 && $input =~ m{\G\s*(\w+)\s*=\s*([\w0-9]+)}cgs) { $perl .= qq{$1=>'$2'}; $state = 2; # simple parameter } elsif($state == 1 && $input =~ m{\G\s*([\w0-9]+)}cgs) { $perl .= qq{'$1'}; $state = 2; # comma to seperate parameters } elsif($state == 2 && $input =~ m{\G\s*,}cgs) { $perl .= ', '; $state = 1; # end of parameter list } elsif(($state == 1 || $state == 2) && $input =~ m{\G\s*\)}cgs) { $perl .= ");\n"; $state = 0; # syntax error or end of input } else { return 1 unless $input =~ m{\G.}cgs; print "operation name expected\n" if $state == 0; print "parameter expected\n" if $state == 1; print "comma or end of parameter list expected\n" if $state == 2; return 0; } } $perl .= qq< return 1; } >; eval $perl; if($@) { # display diagnostic information to user }We're using the \G regex metacharacter that matches where the last global regex on that string left off. That lets us take off several small bites from the string rather than having to do it all in one big bite. The flags on the end of the regex are:
$money = $player->query_money(); if($player->query_max_money() < $x + $payout) { $player->set_money($money + $payout); $nickels_on_floor = 0; } else { $nickels_on_floor = $money + $payout - $player->query_max_money(); $player->set_money($player->query_max_money()); }No matter which way we make the set_money() function work, we're doomed. If it enforces a ceiling, then we have to query again to see if the ceiling was enforced. If it doesn't enforce a ceiling, then we have to check each and every time we access the value and enforce it ourselves. The result is one or two of these sequences of logic will get strewn around the program. The problem is that the client needs something slightly more complex than the server is prepared to provide. We could, and perhaps should, make the object we're calling, $player, return an array, including success or failure, how much money actually changed hands, how much more they could carry. This would go with the approach of providing way more information than could ever be needed. This leads to bloated code and logic that we aren't sure whether or not is actually being used, leading to untested code going into production and becoming a time-bomb for the future, should anyone actually start using it. Less dramatically, we could modify the target object to return just one more piece of information when we realize we need it. This leads to a sort of feature envy, where the server is going out of its way to provide things in terms of a certain clients expectations, causing an API that is geared towards a particular client and incomprehensible to all else. Also, there is temptation to write something like:
package Util;Beware of Utility, Helper, Misc, etc packages. They collect orphan code. The pressure to move things out of them is very low, as they all seem to fit by virtue of not fitting anywhere else. They grow indefinitely in size because the class of things that don't seem to belong anywhere is very large. The effect snowballs as the growth of other objects is stymied while the "Utility" package booms.
package Casino; use ImplicitThis; ImplicitThis::imply(); sub pay_out { # this method would go in $player, since it is mostly concerned with $player's variables, # but we don't want to clutter up $player's package, and we don't know if anyone else # will ever want to use this code. my $player = shift; my $payout = shift; my $money = $player->query_money(); if($player->query_max_money() < $money + $cost) { $player->set_money($money + $payout); $nickels_on_floor = 0; } else { $nickels_on_floor = $money + $payout - $player->query_max_money(); $player->set_money($player->query_max_money()); } }Associating methods with our client object that reasonably belong in the server object ($player, in our case), isn't always the worst solution. In fact, if you put them there and leave them until it is clear that they are needed elsewhere, you'll find that either they are globally applicable, they only apply to this client, they apply to a special case of the client, or they apply to a special case of the server.
10 let a=a+1 20 if a > 10 then goto 50 30 print a:print "\n" 40 goto 10 50 stop foreach my $a (1..10) { print "$a\n"; }Despite the systematic banishment of these languages *, people still find ways to write code that has this problem on a large scale:
while(1) { if(@queue) { dosomething(); } }This example applies to threaded code, but non threaded code can fall prey as well:
while(! -f $file) { } # do something with $file hereBoth of these examples attempt to use 100% CPU resources. In the best case, you make everything sluggish. Worst case, you never release the CPU so the thing you're waiting for happens. On different systems, the results are different, too. Some threads preempt. Others never take the CPU back until you call yield() or an IO function! Writing code like this is the quickest way to make things work on some systems but not others.
$fooLogic->doSomething($stuff, $morestuff);
$fooLogic->doAnotherThing($stuff->querySomeThing(), $morestuff->queryOtherThing());Hack through this overgrown jungle long enough, and paths will emerge - or you'll discover that there is no where to go and curse yourself for abandoning civilization.
package DoEverythingThenSome; my $foo; my $bar; sub nastysub1 { $foo += 300; ... } sub nastysub2 { ... } ... sub nastysub300 { ... }Okay, nastysub1 through 100 are getting the boot:
package FooBarUlator; use ImplicitThis; ImplicitThis::imply(); sub new { my $type = shift; my %args = @_; bless { foo => $args{foo}, bar => $args{bar} }, $type; } # cut and paste directly from DoEverythingThenSome: sub nastysub1 { $foo += 300; ... } sub nastysub2 { ... } ... sub nastysub100 { ... }Functions that use package-global variables (eg, my variables declared at the outer most level, the file scope) that should be migrated to objects present one huge problem: tracking down each and every use of the variable and changing it to a hash dereference to work with Perl's object implementation. ImplicitThis lets you cheat your way out of this chore by making it look all the same to you. By creating a package with a constructor and instance data, we're opening up the possibility of one-to-many relationships against our object: several independent instances of FooBarUlator can exist at once, each with different values for $foo and $bar and any other fields it defines.
my $appointment = $sunday->query_scheduler()->schedule_appointment($sunday, '9:00am'); if(!$appointment) { warn "failed to book appointment. sorry, boss."; }
package WhineyScalar; sub new { tie $_[1], $_[0]; } sub TIESCALAR { bless \my $a, shift; } sub FETCH { my $me = shift; $$me; } sub STORE { my $me = shift; my $oldval = $$me; $$me = shift; (my $package, my $filename, my $line) = caller; print STDERR "value changed from $oldval to $$me at ", join ' ', $package, $filename, $line, "\n"; } 1;[30]
use WhineyScalar; new WhineyScalar my $foo; $foo = 30; # this generates diagnostic output print $foo, "\n"; $foo++; # this generates diagnostic outputUsing tie on a scalar, we can intercept attempts to store data, and generate diagnostics to help us track down what unexpected sequence of events is taking place.
x*5 + 10 = x*2 + 32Refactored:
15 = 5 * 3When programming, the simplest thing you can break things down into is a matter of opinion. Or rather, it is a matter of opinion what programs are composed of. Instructions? Expressions? Functions? Objects? Modules? Some languages represent everything with an object (Smalltalk, for instance). This lets us abide by an executive ruling that objects are the fundamental building block, which pleasantly closes the question. Perl being pragmatic, programs are built in strata. Packages are built on objects are built on functions are built on expressions. Just like a polynomial expression, these combine in different ways to create something more complex.
print 10+32;You move on to write reusable pieces of code needed to build things just more complex than the simplest structures.
sub indent { print ' ' x $_[0], $_[1], "\n"; }Functions let you repeat a bit of logic without having to repeat it in the program. This lets you repeat it an unknown number of times, and makes it easy to run it or not run it under different variable conditions.
100 OLDA=A 110 A=50 120 GOSUB 200 130 PRINT "The result is: ";PRINT $A 140 A=OLDA ... 200 A=B*100 210 RETURNWhat seems like the simple solution, stashing data somewhere and giving it a name, turns out to be a nightmare. Subroutines couldn't safely be written that that would perform a given operation on any give piece of data. Later versions of BASIC of course changed this, but not until a few new generations of langauges came and whooped it one.
opendir my $d, "arts/" or die $!; my $processedflag = 0; my $file; FILE: while($file = readdir($d)) { # attempt to process the file, set $processedflag if successful handle_delete(); } sub handle_delete { unlink $file if $processedflag; $processedflag = 0; }Later on, we decide to add the ability to handle all of the subdirectories of the given directory, a change of heart brought on by an interaction with an individual who expects you to code things he can't even remember, much less communicate.
sub process_directory { my $dir = shift; opendir my $d, $dir or die $!; my $processedflag = 0; my $file; FILE: while(my $file = readdir($d)) { if(-d $file) { process_directory($file); } else { # attempt to process the file, set $processedflag if successful # we now have to explicitly pass our arguments! handle_delete($file, $processedflag); } } sub handle_delete { my $file = shift; my $processedflag = shift; unlink $file if $processedflag; $processedflag = 0; } process_directory('arts/');If we hadn't changed the call to handle_delete() to pass arguments, and modified the subroutine handle_delete to use local variables instead of global variables, $processedflag could hold left over data, altered by a call made from process_directory() to process_directory() that returned. It used to be that each instance of a running programmer had one main loop that used these variables. Now, this is a function that could be called several times, and even have several copies running at the same time, as it calls itself. Data that was specific to the program is now specific to an individual function call. Our variable definitions reflect this change.
# Before: use vars qw/$foo/; sub bar { do_something_with($foo); } sub baz { print $foo, "\n"; } # After: do { my $foo; *bar = sub { do_something_with($foo); }; *baz = sub { print $foo, "\n"; }; };The syntax changes on the function definitions. Thats annoying. This syntax is needed so that the functions are generated then and there, which ties them to their surrounding context - including the lexical variables - but in a fast, efficient way. Only bar() and baz() can see $foo now. You could say that they are the only things in $foo's scope. The reduced scope makes the grouping visible, which makes the association obvious.
# store things in the correct array - ugly print "Who should get a cookie? "; my $name = <STDIN>; print "What kind of cookie should they get? "; my $cookie = <STDIN>; push @{'cookies_for_'.$name}, $cookie; # store things in the correct array - clean my $peoples_cookies = {}; print "Who should get a cookie? "; my $name = <STDIN>; print "What kind of cookie should they get? "; my $cookie = <STDIN>; push @{$peoples_cookies->{$name}}, $cookie;The already confusing reference syntax becomes even more confusing when you want to refer to something other than a scalar:
# scalars are easy: my $cookie = $peoples_cookies->{'fred'}->[0]; # but refering to an array or hash inside of a data structure is confusing: my @cookies = @{$peoples_cookies->{'fred'}};The syntax for using datastructures is remarkably like the syntax for accessing the symbol table directly. The difference is what goes inside of the request to dereference:
@{...something...} # this is how you dereference something as an array %{...something...} # this is how you dereference something as a hashA "soft reference" or "symbolic reference" is a reference used this way with an expression that contains the name__ of the variable that contains the data.
open my $wordsfile, '/usr/share/dict/words' or die $!; my @words = <$words>; close $wordsfile; my $something = \@words; print "The words I know are: ", @{$something}, "\n";The "my" in this example is important - otherwise our variable will be overwritten if we do this in a loop, and if we exit out of a block, it may vanish entirely.
$peoples_cookies->{'fred'}->[0] = 'sugar cookie'; print *{'cookies_for_fred'}, "\n"; # theres nothing there, and no warningPerl will stop you if you use "soft" references (directly access the symbol table) while use strict is on.
<Spike[y]> does anyone know if theres a way to name a Hash like this: %hash$i{$a} = $blah; ? <hull> i dont understand:P <hull> cant you use %hash{$i}{$a} in your program? <Spike[y]> can you? i'm trying to make the name of a new hash go up each time it hits a certian thing (ie. go from %hash1 to %hash2) <hull> hmm <hull> like, in a for loop? <hull> for (my $i=0; $i<$k; $i++) { $hashname{$i}{$k} = "R0xx0R!"; } <hull> you can do it like that:P <Spike[y]> yeah! <Spike[y]> i can?! <Spike[y]> wierd <Spike[y]> it tells me its an error <Yaakov> $ not % <cp5> ${"hash$i"}{$a} = $blah <Spike[y]> hm .. ok <Yaakov> NO! <Yaakov> NO NO NO * cp5 runs <Yaakov> DON'T USE SYMREFS! <perl-fu> ew... the green apple squirts <Yaakov> DEATH <perl-fu> AAAAAAAAAh!! <Spike[y]> ?? <Yaakov> Use a hash of hashes <Yaakov> read perldoc perldsc <hull> Yaakov: hash of hashes... sorta like multi-dimensional hash, uh? <scrottie> http://wiki.slowass.net/?SoftrefsToHash <scrottie> hull, you've been here before
my $tests_skipped = 0; my $subtests_skipped = 0; sub runtests { my(@tests) = @_; my($test,$te,$ok,$next,$max,$pct,$totbonus,@failed,%failedtests); my $totmax = 0; my $totok = 0; my $files = 0; my $bad = 0; my $good = 0; my $total = @tests; ... foreach my $file (@tests) { $ok = $next = $max = 0; @failed = (); my %todo = (); my $bonus = 0; my $skipped = 0; my $skip_reason; ... } } # Refactored: sub runtests { my(@tests) = @_; my(%failedtests); # Test-wide totals. my(%tot) = ( bonus => 0, # $totbonus max => 0, # $totmax ok => 0, # $totok files => 0, # $files bad => 0, # $bad good => 0, # $good tests => scalar @tests, # @tests sub_skipped => 0, # $subtests_skipped skipped => 0, # $tests_skipped ); ... foreach my $file (@tests) { ... # state of the current test. my %test = ( ok => 0, # $ok 'next' => 0, # $next max => 0, # $max failed => [], # @failed todo => {}, # %todo bonus => 0, # $bonus skipped => 0, # $skipped skip_reason => undef, # $skip_reason ); ... } ... }Credits: MichaelSchwern
my $a = new MapDaemon($mapob); my $i = shift(); $a->send_region($i, 10, 15, $x2, $y2);...would become...
my $a = new MapDaemon($mapob); my $i = shift(); my $send_region = sub { $a->send_region($i, 10, 15, shift(), shift()); }; $send_region->($x2, $y2);In this example, $i and $a are lexically bound to the variables of the same name created just before it. If you change the value of these variables, it affects the code reference you've created in $send_region. If you pass $send_region off, or if you return it, $i and $a will continue to hold their values. 10 and 15 will be hardcoded in and cannot be changed. The two argument positions that contain the keyword shift() will take their arguments from what is passed to the reference that is created. This illustrates three different ways of getting values into the code reference. Each has its purpose and its place.
my $a = new MapDaemon($mapob); my $i = shift(); my $send_region_x = sub { $a->send_region($i, $x1, $y, $x2, $y); }; my $send_region_y = sub { $a->send_region($i, shift(), $y, shift(), $y); }; foreach $x1 (1..100) { foreach $x2 (1..100) { $send_region->(); # $send_region->($x1, $x2) would do exactly the same thing } }The advantage of the approach that uses $send_region->() is that we could set values for $x1 and $x2, and pass $send_region out to another routine that could supply more arguments, without actually having to explicitly pass $x1 and $x2 along with $send_region.
#!/usr/bin/perl my $con = connect_to_server(@ARGV); my @treasure; my $x; my $y; my $bot; my @bots; # ... sub process_treasure { # build a list of treasure my $line = $con->('read'); while($line =~ m/\G(\d+) (\d+) (\d+) (\d+) ?/g) { # read from server: id destx desty weight ..., eg: 17 133 28 50 89 11 57 78 # add fields for the $x, $y location of the treasure and the (optional) object that holds it, if any push @treasure, [$x, $y, undef, $4, $2, $3, $1]; } } sub available_treasure { # return a list of treasure at our location my @result; foreach my $treasure (@treasure) { # huh? push @result, $treasure if $treasure->[0] == $x and $treasure->[1] == $y and !$treasure->[2]; } return @result; }Lets fancy for a moment that we have a whole bunch of code that looks like this. We don't want to have to keep looking up which fields represent what, but we don't have time right now to change all of the code for the privilege of using symbolic names. In fact, if we had to change the existing code in order to get symbol names for the fields, we wouldn't bother. It would be more work than continuing on this path. If we did convert it, our first intuition would be to turn the arrays into hashes. Here is another approach:
# in Treasure.pm: package Treasure; sub new { my $type = shift; bless [@_ || (0)x7], $type; } sub x :lvalue { $_[0]->[0] } sub y :lvalue { $_[0]->[1] } sub bot :lvalue { $_[0]->[2] } sub weight :lvalue { $_[0]->[3] } sub destx :lvalue { $_[0]->[4] } sub desty :lvalue { $_[0]->[5] } sub id :lvalue { $_[0]->[6] } 1; package Treasure::Chest; sub new { bless $_[1] || [], $_[0]; } sub get { my $t = $_[0]->[$_[1]] ||= new Treasure; bless $t, 'Treasure'; $t->id() = $_[1]; $t; } sub all_treasure { my $self = shift; map { $self->[$_] ? $self->get($_) : undef } (0..scalar(@$self)-1); } 1; # back in our main program: use Treasure; my $treasurechest = new Treasure::Chest(\@treasure); # lets see available_treasure() again, rewritten to use the named fields: sub available_treasure { # return a list of treasure at our location my @result; foreach my $treasure ($treasurechest->all_treasure()) { push @result, $treasure if $treasure->x == $x and $treasure->y == $y and !$treasure->bot; } return @result; } sub take_treasure { my $treasureid = shift; my $treasure = $treasurechest->get($treasureid); # associate the treasure with our bot: $treasure->bot = $bot; # add the treasures weight to our weight: $bot->[3] += $treasure->weight; }With just a few short lines of code, we've written an object oriented wrapper for our data structure that doesn't get in the way of using our data structure normally. The old functions continue to work, and we can write new functions in the new style, or the old style, or a mixture of styles. Of course, when we have time, we may want to go clean up the old code. Perhaps we've been meaning to rewrite it all anyway. Who has ever heard of object oriented programming where introducing a new object type doesn't require changes to all of your data reference?
$treasure->bot() = $bot;...which looks highly unnatural. We were assigning to a function before, but it didn't look like it because the function call didn't have parenthesis on the end. How can you possibly assign to a function? :lvalue functions never return a string constant or the result of an arithmetic expression. They do give as their result a variable, or an expression that references a primitive variable. It is this variable that is assigned to. The lvalue function can pick which variable is assigned to, or even let the user assign to the result of another function if that function is also lvalue. substr(), for instance, is lvalue. That means both of these are legal:
sub foo :lvalue { $_[0] ? $a : $b } foo(0) = 10; # assign 10 to $a foo(1) = 30; # assign 30 to $b sub bar :lvalue { substr($a, 10, 5) } bar() = "xyzzy";Note that we don't use return. Return makes a copy, and that would damage out intentions. In other words, return() isn't lvalue. What is assigned to is the last expression in the block.
$treasure->{'wegiht'} = 30; # this would go unnoticed by the compiler $treasure->wegiht = 30; # this would be caught the first time it ranCatching the error right away helps us quickly track down the problem. Using a hash index, we might be confounded why the weight wasn't updating correctly, and search all over the program, not knowing which part of the code that accesses it to suspect. Some typos are hard to notice, especially after you've been staring at code all day. I've spent hours trying to find a logic error only to discover a subtle typo. Its annoying. Another advantage of using the OO accessor approach is that we can do value checking, to make sure the new value itself isn't bogus or empty. We can also trigger side effects, and update other global state of the object, or notify observer objects of the state change, or countless other behaviors.
if($treasure->bot) { $x = $treasure->bot->x; $y = $treasure->bot->y; } else { $x = $treasure->x; $y = $treasure->y; }This is error prone, and tedious. It doesn't fit at all with our laziness. If we change the definition of the x() and y() methods in Treasure.pm, we can write this OnceAndOnlyOnce:
sub x :lvalue { $_[0]->bot ? $_[0]->bot->x : $_[0]->[0]; } sub y :lvalue { $_[1]->bot ? $_[0]->bot->y : $_[0]->[1]; }In the future, asking for $treasure->x or $treasure->y gives the correct, expected value. Since :lvalue methods behave just like variables, we can do things like this:
$treasure->y-- if $dir eq 'north'; $treasure->y++ if $dir eq 'south'; $treasure->x++ if $dir eq 'east'; $treasure->x-- if $dir eq 'west';Even though x() and y() are methods, the ++, --, and all other operators work on them as if they were a variable. Perl actually performs those operations on the last variable mentioned in the expression, before the method returns. An :lvalue function can itself be the last thing mentioned in an :lvalue function: these rules are recursive. See AccessorsPattern for more examples of this technique.
($angle, $distance) = Converter::cartesian_to_polar($treasure->x, $treasure->y);... if instead we could write:
($angle, $distance) = $treasure->to_polar();The arguments are built in to the function!
sub set_x { if($_[0]->bot) { die "Cretin! I've TOLD you to check ->bot() before trying to set the location! It is illegal to set the location on an object thats being carried!"; } # "x" is the first array position $_[0]->[0] = $_[1]; } sub query_x { return $_[0]->bot ? $_[0]->bot->x : $_[0]->[0]; }query_x() is almost exactly like our old x(), except it isn't an :lvalue function. To change our X location, someone needs to first check bot(), and if there isn't a value there, then set it using set_x(). This is our procedure and we're enforcing it. A rogue programmer could always meddle around inside of our datastructure and ignore the OO interface, but we can only assume he knows what he is doing if he does that. There is a technique for hiding your data using lexically scoped variables that prevents this trick from working: see LexicalsMakeSense for some examples. Even that isn't secure: using PadWalker module, people can investigate and alter lexicals that would otherwise be hidden from them. If you truly want to enforce a strict policy for using your code from untrusted code, use the Safe module. This locks them into a sandbox and lets you define what they can do there.
package Class::Null; use vars '$VERSION'; $VERSION = '1.02'; my $singleton; sub new { $singleton ||= bless {}, shift } sub AUTOLOAD { *{$AUTOLOAD} = sub {}; undef } 1;See Also: ExplicitTypeCaseAnalysis, SingletonPattern
sub play { my $me = shift; my $investor = shift; $investor->isa('InvestmentBanker') or die; my $stock = shift; $stock->isa('Stock') or die; my $amount = shift; $stock->set_quantity($amount); $me->{$investor}={$stock}; }This is the play() method from our TradingFloor.pm example. We mentioned this briefly in ObjectOrientationOrientation, but it bears lassification [34]. Each object passed in is tested to see if they fit that type. For ->isa(), either being blessed into that package directly, or inheriting from that package will work. In the case of references, check the reference type directly:
my $arrayref = shift; ref $arrayref eq 'ARRAY' or die;Given a bless object, ref() will return its blessed type, but this usual isn't what we want: see CheckingTypeInsteadOfMembership. For references, it will return 'ARRAY', 'HASH', 'SCALAR', 'GLOB', or one of several other possible values XXX.
"...a brief outline or explanation of the order to be pursued...". The strong analogy between the sequences of instructions given to computers and the planned ordering of events in a musical or theatrical performance lead early British coders to coin the phrase.
# Haskell qsort: qsort [] = [] qsort (x:xs) = qsort elts_lt_x ++ [x] ++ qsort elts_greq_x where elts_lt_x = [y | y <- xs, y < x] elts_greq_x = [y | y <- xs, y >= x] # perl qsort: sub qsort { !@_ ? () : (qsort(grep { $_ < $_[0] } @_[1..$#_]), $_[0], qsort(grep { $_ >= $_[0] } @_[1..$#_])); } print "output: ", join ' ', qsort(51,382,5,28,382,28,6,3,92,8), "\n";The Haskell can reportedly be read as: define the result of qsort'ing the empty set as the empty set itself. qsort'ing one or more elements puts the first element in x and the rest in xs. qsort is defined recursively. Each pass, it divides up the work. When it has been broken down as far as it can be, all of the parts are reassembled, in order. The basis for breaking it down at each stage is dividing xs into things less than x and things greater than or equal to x. Each of these subsets are themselves run through qsort, with x itself stuck in the middle.
!@_ ? () : ...This is a matter of explicit test placed before the recursive calls. I said it was similar, not identical. The Haskell version shuns anything explicitly sequential. We don't built any local definitions for elts_lt_x or elts_greq_x, though we could, using anonymous subroutines. Instead, we just use a couple of grep calls inline to filter our argument list, first for everything less than the first argument, then everything greater than or equal to the first argument. The first argument, $_[0], is sandwiched in the middle. @_[1..$#_] is an array slice. It returns a new array created from @_ with specified elements. Here, we're listing elements 1 through the number of elements in the array. $#_ contains the number of elements in @_, and the .. operator returns all of the integers between 1 and that. This just lets us skip the first element, since we're explicitly using it as a middle value and sandwiching it in the middle.
sub get_save_filter { my $ext = shift; my $stream = shift; return sub { # do something with $stream here } if $ext eq 'gif'; return sub { # do something with $stream here for pngs } if $ext eq 'png'; return sub { # do something with $stream here for jpgs } if $ext eq 'jpg'; } print "Enter a filename to save the image:\n"; my $filename = <STDIN>; # find file extention: png, jpg, gif, etc (my $ext) = $filename =~ m/\.([a-z]+)/; my $save_filter = get_save_filter($ext); open my $f, '>', $filename; print $f $save_filter->($image_data);In this example, we're using data to pick a code pointer. $save_filter->() executes whatevercode $save_filter refers to. Both calling a polymorphic object method, and using Perl's eval on a string, also have the same effect. The code that gets executed may not find all of the data it wants in a suitable state, and we may not have realized the possibility of the sequence of events that could lead to the situation.
$dispatch_table->{$request}->();...and...
GOTO REQUEST*1000In both cases, two things are clear. How we arrive at the actual code we're shooting for depends on only one variable, not every variable involved in the computation, and it isn't clear which variables will be used, and what their acceptable ranges are, once we arrive. Our data dictates the direction of the program, but our data and our program keep each other at quite a distance.
# procedural for(my $i=0; $i<scalar @arr; $i++) { ...do something to $arr[$i]... } # functional @arr = map { ...do something to $_... } @arr;map() has it's own termination condition built in: when there are no more array elements to process. map() also returns the output data as a list, avoiding the need for temporary storage - avoiding temporary storage avoids explicit sequencing of steps. Avoiding explicit sequencing of code avoids corner cases, and more imporatantly, avoids invloving the state of the program in the flow of the program - a sin I equated to the computed goto.
@arr = grep { $_ % 3 == 0 } (1..10000000); # out of memory foreach my $i (@arr) { do_something($arr[$i]); } # ... would be implemented internally using LazyEvaluation something like... my @arr; my $gen; do { my $counter; $gen = sub { $counter++; }; }; while(my $i = $gen->()) { do_something($i); }Code wouldn't be written this way, but this demonstrates what goes on internally. Perl does implement LazyEvaluation in a few places, including the (1..1000) construct. LazyEvaluation makes dealing with "infinite lists" possible.
Language::Functional introduces more operators like sort() and map(), designed to build algorithms rather than "procedures". Barrowing from Language::Functional documentation, some of the highlights are:
# Until p f x # Keep on applying f to x until p(x) is true, and then return x at that point. eg: $x = Until { shift() % 10 == 0 } \&inc, 1; # 10 # iterate f x # This returns the infinite list (x, f(x), f(f(x)), f(f(f(x)))...) and so on. # Outputs [1, 2, 4, 8, 16, 32, 64, 128] $x = take(8, iterate { shift() * 2 } 1); # And xs # Returns true if all the elements in xs are true. Returns false otherwise. # Note the capital A, so as not to clash with the Perl command 'and'. You # should not try to And an infinite list (unless you expect it to fail, as it # will short-circuit). $x = And([1, 1, 1]); # 1 # Or xs # Returns true if one of the elements in xs is true. Returns false otherwise. # Note the capital O, so as not to clash with the Perl command 'or'. You may # try to Or an infinite list as it will short-circuit (unless you expect it # to fail, that is). $x = Or([0, 0, 1]); # 1 # any p xs # Returns true if one of p(each element of xs) are true. Returns false # otherwise. You should not try to use with an infinite list (unless you expect it # to fail, as it will short-circuit). $x = any { even(shift) } [1, 2, 3]; # 1 # all p xs # Returns true if all of the p(each element of xs) is true. Returns false # otherwise. You may try to use with an infinite list as it will short-circuit # (unless you expect it to fail, that is). eg: $x = all { odd(shift) } [1, 1, 3]; # 1 # elem x xs # Returns true is x is present in xs. You probably should not do this with # infinite lists. Note that this assumes x and xs are numbers. $x = elem(2, [1, 2, 3]); # 1 # minimum xs # Returns the minimum value in xs. You should not do this with a infinite list. $x = minimum([1..6]); # 1 # maxiumum xs $x = maximum([1..6]); # 6 # sum xs $x = sum([1..6]); # product xs $x = product([1..6]);List::Utils predates but runs in the same vien with some overlap. List::Util is by Graham Barr. Documentation barrowed from List::Util:
# reduce BLOCK LIST # Reduces LIST by calling BLOCK multiple times, setting $a and $b each time. # The first call will be with $a and $b set to the first two elements of the # list, subsequent calls will be done by setting $a to the result of the # previous call and $b to the next element in the list. $foo = reduce { $a < $b ? $a : $b } 1..10 # min $foo = reduce { $a lt $b ? $a : $b } 'aa'..'zz' # minstr $foo = reduce { $a + $b } 1 .. 10 # sum $foo = reduce { $a . $b } @bar # concatReturns the result of the last call to BLOCK. If LIST is empty then undef is returned. If LIST only contains one element then that element is returned and BLOCK is not executed.
package Color; sub new { my $type = shift; bless { }, $type; } sub set_color { my $me = shift; my $color = shift; $me->{'color'} = $color; } sub get_color { my $me = shift; return $me->{'color'}; } package main; use Color; my $c1 = new Color; $c1->set_color('green'); my $c2 = new Color; $c2->set_color('blue'); print "c1: ", $c1->get_color(), " c2: ", $c2->get_color(), "\n";Even though both $c1 and $c2 are instances of the "Color" object, they have different values. We call these variables "instance variables" because the variable is associated with a specific instance of the class.
# java: public void mouseDown(Event e, int x, int y) { lastx = x; lasty = y; return true; } # perl: sub mouseDown { my($this, $event, $x, $y) = @_; $this->{'lastx'} = $x; $this->{'lasty'} = $y; return 1; }$this->{'lastx'} is hardly a worthy substitute for being able to merely say $lastx and have the language remember the fact that $lastx is an instance variable. This requirement is going a long way towards making our little program ugly. For longer functions, we can make some symbol table aliases to allow us to access things inside of a hash or array using a short name:
sub mouseDown { my($this, $event, $x, $y) = @_; local *lastx = \$this->{'lastx'}; local *lasty = \$this->{'lasty'}; # now we can refer to $this->{'lastx'} merely as $lastx, just like Java and C++! $lastx = $x; $lasty = $y; }This just makes the problem worse for shorter functions, cluttering them with unneeded syntax. As a compromise, you can use this trick for large functions where instance variables are referenced over and over again, and for short functions, use the plain old hash dereference syntax that you already know and tolerate. Don't like those options? Me either. Lets see what else we can dig up:
package Yawn; sub public { local $this = shift; my $coderef = pop; my @fields = keys %$this; my $field; FIELD: $field = pop @fields; local *{$field}; *{$field} = \$this->{$field}; goto FIELD if(@fields); $coderef->(@_); } sub private { caller(1) eq PACKAGE__ or die sprintf "Cannot invoke private method %s from outside %s", (caller(1))[3], PACKAGE__; public(@_); }Including these methods at the top of your class will allow you to abbreviate method classes:
sub set_x { private @_, sub { $x = shift; }; }Call public() or private() with the arguments and a subroutine to execute. public() will run the passed subroutine unconditionally, while private() will only run it if your method was invoked from within the object. If another object or package tries to invoke your private method directly, it will generate an error message:
Cannot invoke private method Yawn::setb_x from outside Yawn at foo.pl line 17.Additionally, you don't need to say $me->{'x'} to get at your "x" field: you can refer to it directly as $x. For each value in the object hash, a local temporary alias is set up with the same name. $this is set to $me in the same way, which is similar to what other languages provide.
package Foo; use ImplicitThis; ImplicitThis::imply; sub new { my $type = shift; my %options = @_; my $me = { x => $options{'x'}, y => $options{'y'}}; bless $me, $type; } sub setx { $x = $shift; } sub get_x { return $x; }ImplicitThis::imply() is called separately because ImplicitThis.pm needs to wait until your package finishes loading before taking action. When imply() is run, it looks through your name table, finds all of the functions, and puts a thin wrapper around them. The wrapper creates temporary aliases to all of the instance variables, once again giving them the same name.
package SerialNumbered; my $globalSerialNumber; sub new { my $type = shift; $serialNumber++; my $me = {@_, SerialNumber=>$serialNumber}; bless $me, type; }This example keeps a serial number in a variable. Every object has access to this, in addition to their own "SerialNumber" instance variable.
print ${ref($obj).'::globalSerialNumber'};This assumes that $obj is an object of type SerialNumbered, and $globalSerialNumber were a "local" instead of a "my" variable.
foreach my $i (qw(name price quantity)) { my $field = $i; *{"get_$field"} = sub { my $me = shift; return $me->{$field}; }; *{"set_$field"} = sub { my $me = shift; @_ or die "not enough arguments to set_$field, stopped"; $me->{$field} = shift; return 1; }; }For each of "name", "price" and "quantity", we create an anonymous subroutine that binds to the current state of my variables. This is called a LambdaClosure - see LexicalsMakeSense. We go on to name these by dropping them into the symbol table using the glob syntax. They then work as normal methods. When invoked, they are still bound to the variable that was created with my when they were created in the for loop. They're able to use this variable to pick out the correct field. [42]
$stock->{'price'} *= 2; # Rather than the more OO: $stock->set_price($stock->get_price() * 2);Of course, if anyone does that to our neat little package, they void the warranty. Having an accessor, even if it is just a stand-in for having access to the variables directly, keeps our options open about changing our implementation, and thats what this is all about.
$foo->query_ob()->add($baz->query_qux());The only operator this code uses is ->, the method call operator. Compare this to normal, non ObjectOriented code:
$foo{ob}->{bar} += $baz{qux};While the basic arithmetic, logic, and other operators represented by symbols in languages (especially in Perl) account for 99% of what people want to do at any given moment, reading OO code gives you the impression that the only one that hasn't been robbed of its usefulness is the -> operator.
# before: direct access to instance variable $ob->{foo}; # Perl ob->foo # Java # after: using an accessor $ob->foo() # Perl ob->foo() # JavaIn Perl, style dictates that all variables have accessors, and that instance variables never be diddled directly. Both of these pose the problem of differing syntax for instance variables versus method calls. The uselessness of all operators except -> is a closely replated problem.
#!/usr/bin/perl package Cart::Item; sub TIESCALAR { die unless(ref $_[1] eq 'CODE'); my $foo=$_[1]; bless $foo, $_[0]; } sub STORE { my $me=shift; $me->(@_); } sub FETCH { my $me=shift; $me->(); } sub new { my $class = shift; my $name_value; my $me = { }; tie $me->{'name'}, PACKAGE__, sub { $name_value = shift if @_; $name_value; }; # repeat for price and quantity my $color_value; tie $me->{'color'}, PACKAGE__, sub { die "I *HATE* blue" if $_[0] eq 'blue'; $color_value = shift if @_; return $color_value; }; bless $me, $class; } # create accessors as above, slightly modified foreach my $i (qw(name price quantity)) { my $field = $i; *{"get_$field"} = sub :lvalue { my $me = shift; $me->{$field}; }; *{"set_$field"} = sub { my $me = shift; @_ or die "not enough arguments to set_$field, stopped"; $me->{$field} = shift; return 1; }; }A lot of this is out of the scope of this chapter. You can read on, you can employ faith and cut and paste this into your code, or you can just rest assured that there is a solution to the "-> is the only useful operator" problem.
$datastructure{'color'} = \$color;The backslash operator takes a reference of a scalar, hash, array, etc. We store this reference in something else. Code, created by with the sub { } mechanism, can store references, too. It looks a little bit different:
my $foo_value; my $foo_logic = sub { # code $foo_value; # code };There are two secret ingredients to make this work. First, the value that we want to keep a reference to in our code must be declared using "my". Second, we use that variable normally in our sub { } block from a place where that variable is visible. This is before the end of the code block that the variable was defined in - be it a subroutine, eval, do. If it was defined outside of any of those, it is visible until the end of the file. If we define $color_value and $name_value inside of the subroutines, they would get created each time these subroutines are evaluated, then forgotten when they returned. By declaring the variables outside, the subroutines hold on to already existing variables, and they retain their values from call to call. Thats really all there is to it!
$ob->get_value() += $ob->query_value() * $economy->query_inflation();2. If someone completely bypasses our accessors, it doesn't matter one bit: our logic still runs.
$ob->{'color'} = 'blue'; # this blows up because we're able to trap itSee Also
die unless $ob->isa('Number::Complex'); or more typically: if(!$ob || $ob->error) { print "well that didnt work\n"; }When variables are created to hold exactly one datatype, we often do a lot of checking to make sure thats the case. This clutters up code. Perl is difficult enough to understand without clutter.
# in Scalar/Typed/Factory.pm: package Scalar::Typed::Factory; sub import { my $package = shift; my $referencetype = shift or die PACKAGE__ . ': import statement must specify reference type'; $package .= '::' . $referencetype; my $caller = caller; # construct a package for this type. # note that $referencetype is lexically bound to this context *{$package.'::TIESCALAR'} = sub { bless \$_[1], $_[0]; }; *{$package.'::FETCH'} = sub { my $me = shift; $$me; }; *{$package.'::STORE'} = sub { my $me = shift; my $val = shift; ref $val and $val->isa($referencetype) or die sprinf "Incorrect type in assignment: expecting $referencetype: " . "%s in package %s at line %d in %s\n", (caller)[1, 0, 2]; $$me = $val; }; # finally, export a constructor for this new datatype to our users package: (my $constructor) = $referencetype =~ s/[^A-Za-z]//g; $constructor = lc $constructor; *{$caller.'::'.$constructor} = sub ($) :lvalue { tie $_[0], PACKAGE__.'::'.$referencetype; $_[0]; }; } 1; # in the main program: package main; use Scalar::Typed::Factory 'XML::Parser'; use CGI; use XML::Parser; use XML::Parser::Compatiable; xmlparser my $parser = new XML::Parser; # ok xmlparser my $compat = new XML::Parser::Compatiable; # ok xmlparser my $other = new CGI; # dies $parser = new CGI; # dies $parser = 10; # dies $parser = undef; # dies - see IntroduceNullObjectThis is "runtime type checking". Some languages do this checking at compile time. If we had that, it would prevent the program from ever reaching the "die" point: if it were even possible to reach it, the program wouldn't compile.
sub muckWithSomething { caller eq PACKAGE__ or die; my $self = shift; my $arg = shift; # code here using $self and $arg }When you write something yourself, you can usually remember what parts of objects you plan to keep around and which parts you want to keep open to revision. You may, on the other hand, completely forget this, or collaborate with other programmers. This gets you stuck in the trap of having to keep making your object exactly the same way. OO says that the implementation should be separate from the interface. Opting to not allow foreign code to access your internals buys you privacy and freedom to change around your internals.
package Item; sub new { my $type = shift; my %args = @_; $me->{name} = $args{name}; $me->{price} = $args{price}; $me->{image} = $args{image}; $me->{description} = $args{description}; $me->{weight} = $args{weight}; } foreach my $i (qw(name price weight desciption image)) { # create accessor }Then the store adds paper, toner, refill kits, cables, dye sublimation ink, impact ribbons, and new ribbons. Then the store starts giving discounts for toner and inkjet cartridge returns. Then you're asked to give warnings if someone tries to buy a new printer with the wrong kind of paper or ink. Fields are introduced to specify compatibilty and compatibility requirements, as well as return discount amout and eligability.
package Item; sub import { push @{caller().'::ISA'}, PACKAGE__; }Viola! Whenever anyone uses Item.pm, their package will be marked as being (is-a) Item. Should that object ever be tested with ->isa('Item'), it will come back true. We have this one bit of @ISA munging logic in the abstract marker class rather than:
use Item; push @ISA, 'Item';...floating around everywhere.
# example of making sure that an interface is implemented package FooAbstractClass; # rename this with your abstract class sub import { my $caller = caller; eval "use $caller;"; # let them finish loading foreach my $i (grep { defined &{$_} } keys %{__PACKAGE__.'::'}) { die PACKAGE__ . " is listed as a base type in $caller, but the $i method is not implemented!" unless defined &{$caller.'::'.$i}; } }interface.pm by yours truly (look for it on CPAN) implements compile-time interface checking in Perl, similar to the example above, merely with a cleaner interface itself:
use interface 'Class::Struct', 'Clonable';This tells Perl that we must define all of the methods listed in Class::Struct as well as Clonable. If we fail to, we don't compile, and we don't run.
package BetterGronkulator; @ISA=('Gronkulator'); sub new { my $type = shift; my $me = SUPER::new($type, @_); $me->{'spiffyNewThing'} = new BetterGronkulator::SpiffyNewThing; return $me; }We let SUPER::new() (our parent's new() method) create the object for us. If they do it correct, using the type in the first argument, we don't need to bless it. We are now free to modify the object however suits us. Here, we're creating a new object and storing a reference to it. This lets us add to the features of a class without having to rewrite their entire constructor, and risk having it change in a future version. If someone extends us, they can apply this same pattern, and add features to what we do.
# redefining compute_gcd() in a sublass won't affect this code $gcd = compute_gcd($x, $y);They will continue using the methods and functions defined in the same class as themselves, unless they go out of their way:
# this will use the top-most version of compute_gcd() $gcd = $this->compute_gcd($x, $y);A method that does some work, but passes off sub-tasks using the -> syntax is said to be a "template method". This method forms the basis for an operation, but is open-ended. Its behavior can be tailored, customized and specialized in subclasses. This aids in code re-use.
package main; our $this = PACKAGE__; sub template_records { my @records = $this->query_records(); foreach my $record (@records) { $this->per_record($record); my $output = $this->template_record($record); $this->save_record($output); } } sub per_record { # do nothing } sub query_records { # default implementation } sub template_record { # populate a template with data from $record } sub save_record { # save $output to disc }We write this code as a normal program, with no special or even ordinary object-orientedness. Its not in a package. It wasn't created from a constructor. We can't handle multiple instances of our program. The only thing we need to take advantage of template methods is the $this->method() syntax itself. We can fake having an object instance with the our $this = __PACKAGE__; gambit. PACKAGE__ always contains the name of the current package for the module its in. Rather than access an instance of the class, we access the definition of the class directly.
# in MegaCorp.pm: package MegaCorp; our $this = PACKAGE__; @ISA = qw(main); sub per_record { my $this = shift; my $record = shift; if($record->{price} == 0) { $record->{availability} = "Sold Out"; } } 1;We load up the original code, but we also load up MegaCorp.pm, and thats what we use. It barrows all of the logic from the original, but makes some changes to it. The best part is that we can create any number of modules like this and share the main code between all of them. The same code can work different ways for each client, project, customer, and so on.
sub new { my $type = shift; $type = ref $type if ref $type; my $this = { }; bless $this, $type; }Line 3 does the work of making sense out of either a package name or a reference. The normal case is a scalar containing the package name. Thats the case with:
MyPackage->new(); ... and ... new MyPackage;The new case we handle is when someone already has a reference but wants a new one:
$anothermypack = $mypack->new();See Also
($foo = $bar) * 100;In Perl, the result of an assignment is the variable that got assigned to. This lets you chain operations. It is pleasant to be able to do the same thing objects:
$fooOb->setBar($bar)->multiply(100);Instead of returning "1", we return $fooOb back, to be used again.
package WebsafeColors; sub new { ... }; sub getIterator { my $parentThis = shift; return eval { package WebsafeColors::Iterator; # this mini sub-package only knows how to iterate over our data structure @ISA=(Iterator); sub new { my $type = shift; my $this = { currentIndex=>0 }; bless $this, $type; } sub hasNext { my $this = shift; return @{$parentThis->{'colors'}} > $this->{'currentIndex'}; } sub getNext { my $this = shift; die unless $this->hasNext(); return $parentThis->{'colors'}->[$this->{'currentIndex'}++]; } PACKAGE__; }->new(); }The full listing is available at XXX. WebsafeColors::Iterator implements all of the functions required to be an instance of Iterator. If something takes an argument, and insists it implement Iterator, it will accept the result of calling getIterator() on a WebsafeColors object. However, WebsafeColors itself does not implement these methods, or inherit the base abstract class for Iterators. The package that does is contained entirely inside WebsafeColors's getIterator() method. This technique lets you localize the impact of having to provide an interface, and keep code related to supporting that interface together and away from the rest of the code. This supports the basic idea of putting code where it belongs.
use overload '<=>' => sub { my $me = shift; my $them = shift; # return -1 to indicate we go before them # return 1 to indicate we go after # 0 means logically same return $me->foo <=> $them->foo; }, '""' => sub { my $this = shift; return join '.' map { $this->{$_} } qw(field1 field2 field3); };Your object now knows how to stringify itself, and can be sorted and compared to like objects:
my @obs = sort $ob1, $ob2, $ob3;More importantly, you can test objects for equality using a benchmark other than rather or not the two pointers reference the exact same object.
package LinkedList; use ImplictThis; ImplicitThis::imply(); sub new { my $type = shift; bless { next=>'', previous=>'' }, $type; } sub next { return $next; } sub set_next { $next = shift; return 1; } sub previous { return $previous; } sub set_previous { $previous = shift; return 1; } sub append { my $ob = shift; $ob->isa(__PACKAGE__) or die; $next or do { $next = $ob; $ob->set_previous($this); return 1; } $ob->set_next($next); $next->set_previous($ob); $ob->set_previous($this); $this->set_next($ob); return 1; }We can inherit this, but inheriting it multiple times doesn't do us any good: we only ever have one instance of the LinkedList this way - ourselves. Using composition gives us what we want:
package TriceQueuedObject; use LinkedList; use ImplicitThis; ImplicitThis::imply(); sub new { my $type = shift; my $me = { sort_order => new LinkedList, size_order => new LinkedList, save_order => new LinkedList, @_ } # create accessors that defer the action to each object, for each object composing us: # method A: see text below sub next_sort { return $sort_order->next(); } sub previous_sort { return $sort_order->previous(); } sub set_next_sort { return $sort_order->set_next(@_); } sub append_sort { return $sort_order->append(@_); } sub next_size { return $size_order->next(); } sub previous_size { return $size_order->previous(); } sub set_next_size { return $size_order->set_next(@_); } sub append_size { return $size_order->append(@_); } sub next_save { return $save_order->next(); } sub previous_save { return $save_order->previous(); } sub set_next_save { return $save_order->set_next(@_); } sub append_save { return $save_order->append(@_); } # directly return references to objects that compose us: # method B: see text below sub get_sort_order { return $sort_order; } sub get_size_order { return $size_order; } sub get_save_order { return $save_order; }Where it says "method A" and "method B" illustrate two very different approaches to giving users of our object access to the our parts. "Method A" creates all new accessors which do their work by calling accessors in the composing objects. "Method B" simply returns the composing objects and lets the user call the methods directly. For example:
# using method A: $ob->next_sort($ob2); # using method B: $ob->get_sort_order()->set_next($ob2);Which is better? Well, it depends. If your object is merely a container for other objects, B makes more sense. If your object is a Facade, providing a new interface to several objects, A makes more sense. If you consider the objects you contain to be implementation dependent, and you don't want to have to support returning intermediate objects in the future, A lets you hide your implementation better. B makes for shorter code and less typing when the relationship between the objects isn't likely to change.
# a module that exports: use Foo; foo("module Foo does something with this"); # a module that doesn't export: use Bar; my $bar = new Bar; $bar->do_something("module Bar does something with this");If you're only reasonably going to be using one instance of a "Bar" object, why go through the hassle of giving it a name ($bar)? Which object we're talking about when we say do_something("bar!") is unambiguous as well - we only have one Bar object to call it in. The handiness of the first approach is so loved that many modules that provide a strictly OO interface (as in the second example, Bar) also provide a syntactical sugar, exporter interface as well (as in the first example). This is accessed, by convention, as:
use Bar qw(do_something); do_something("module Bar does something without a lot of typing, just like Foo!");Here, we tell Bar just to make the method do_something() available to us. If Bar creates a single instance of an object behind the scenes, so be it - we never have to know.
package Bar; sub import { my $caller = caller; my $foo; my $bar; my %methods = ( get_foo = sub { $foo; }, set_foo = sub { $foo = shift; }, get_bar = sub { $bar; }, set_bar = sub { $bar = shift; }, do_something = sub { $foo . ' and ' . $bar . ' sitting in a tree... ' }, ); foreach my $i (keys %methods) { *{$caller.'::'.$i} = $methods{$i}; } } 1;This example uses LexicallyScopedVariables to generate a scalar for each package that imports our logic. In other words, if multiple packages use Bar, each gets copies of set_foo(), get_foo(), do_something(), and so on, but each package has its own private values for $foo and $bar used by these functions. One packages actions won't step on another packages data.
package Bar; sub new { my $type = shift; my $foo; my $bar; my $object = { get_foo = sub { $foo; }, set_foo = sub { $foo = shift; }, get_bar = sub { $bar; }, set_bar = sub { $bar = shift; }, do_something = sub { $foo . ' and ' . $bar . ' sitting in a tree... ' }, }; bless $type, $object; } sub import { my $caller = caller; my $methods = PACKAGE__->new(); foreach my $i (keys %$methods) { *{$caller.'::'.$i} = $methods->{$i}; } } sub AUTOLOAD { my $me = shift; (my $method) = $AUTOLOAD =~ m/::(.*)$/; return undef if $method eq 'DESTROY'; return wantarray() ? ($me->{$method}->(@_)) : scalar $me->{$method}->(@_); } 1;We're coming in from a lot of angles at once here, but it still isn't very much code. Our former import() logic found its way into new(), with a few changes: we're using a hash reference instead of a hash, and we're blessing it as an object. This object is exceptional: instead of merely containing variables, it contains anonymous code references. These anonymous code references are lexically bound to their data, exactly like our former example. Perl doesn't know how to use this kind of an object, but thankfully we can teach it: the AUTOLOAD() method is used to look up methods on demand. This should be invoked for every method call except for new(), since no other methods are defined. When it is invoked, we get our hash reference passed in, and to get at our anonymous subroutine reference we tucked away in the hash reference, use this syntax:
$me->{$method}->(@_);This looks up the method, by name, in our hash reference, then calls it as a subroutine, passing the current argument list. Viola! A new sort of object that keeps a hash reference of code lexically bound to data rather than just plain old boring data.
*{$caller.'::'.$i} = $methods->{$i};Assinging to a glob (variables that start with an asterisk) is special. Perl figures out what to do with the value we're assigning. If we assign a scalar reference to *whatever, it becomes available as $whatever. If we assign a hash reference to *whatever, Perl makes it available as %whatever. If we assign a code reference to *whatever, it appears as whatever(). We take advantage of this to define functions not in our namespace, but the namespace of the package calling our import() routine. If you examine Exporter.pm, you'll find that this is exactly what it does.
use 5.9.34; print "Perl 6 should be just about ready by now!\n";To write a module that exports functions when use'd, the Exporter offers a convinient way:
package PrintLN; use Exporter; @ISA = qw(Exporter); @EXPORT = qw(println); # symbols to export by default @EXPORT_OK = qw(printpf); # symbols to export on request sub println { print @_, "\n"; } # print a line with a line feed sub printpf { print @_, chr(0x0c); } # print a line with a page feedFunctions named in @EXPORT always get exported to the program that uses us. They can use them (almost) just like normal built in Perl functions. Functions listed in @EXPORT_OK must be requested:
package main; use PrintPL qw(printpf); printpf("This page intentionally left blank.");You could do the same thing manually, of course. Define an import method in your module, like so:
sub import { *{caller().'::printpf'} = sub { print @_, chr(0x0c); }; }This doesn't have a lot to do with Object Oriented programming. It is a staple of Perl programming, so we need to know about it.
use CGI 2.4;This will die unless $CGI::VERSION >= 2.4. Perl checks the target module's $VERSION package global variable variable and verifies that it is equal to or greater than the number you specify. Perl understands "ordinals", comparing 2.4.1 against 2.4.2 does the right thing: 2.4.2 is larger.
5 relational principles ... XXX full-life cycle development ... XXX OSI layers ... XXX CVS FORK & MERGEDivirging copies happen. Refactoring, taken to an extreme, says to refactor the shared module into existance. Is what is good for one program good for another? We don't know, not before hand. A top-down approach fails here.
BACKING OUT CHANGES AND THE LOGCategoryToDo
threads::shared::semaphore - thread::safe semaphores use threads::shared::semaphore; my $s = new threads::shared::semaphore; $s::>up; # Also known as the semaphore V ::operation. # The guarded section is here $s::>down; # Also known as the semaphore P ::operation. # The default semaphore value is 1. my $s = new threads::shared::semaphore($initial_value); $s::>up($up_value); $s::>down($up_value); threads::shared::queue - thread::safe queues use threads::shared::queue; my $q = new threads::shared::queue; $q::>enqueue("foo", "bar"); my $foo = $q::>dequeue; # The "bar" is still in the queue. my $foo = $q::>dequeue_nb; # returns "bar", or undef if the queue was # empty my $left = $q::>pending; # returns the number of items still in the queueB
B::Xref - Generates cross reference reports for Perl programs perl ::MO=Xref[,OPTIONS] foo.plSYSTEM
PeekPoke Sys::CPU Mmap POSIX - Perl interface to IEEE Std 1003.1 use POSIX; use POSIX qw(setsid); use POSIX qw(:errno_h :fcntl_h); printf "EINTR is %d\n", EINTR; $sess_id = POSIX::setsid(); $fd = POSIX::open($path, O_CREAT|O_EXCL|O_WRONLY, 0644); # note: that's a filedescriptor, *NOT* a filehandleBUSINESS
Barcode::Code128 Business::UPS Date::CalcWEB
CGI_Lite CGIDEBUGGING
Carp::Assert traceFunc Test::MockObject Test::Class Text::BalancedMATH
Math::LP::Solve Math::LinearCombination Math::SimpleVariable PDL::R::math Routines from the "R" statistical language. "R" is like "S". Statistics::RegressionNATURAL LANGUAGE
Text::Metaphone Text::English Porter's stemming algorithm Text::NLP Festival::Client Sort::Naturally Text::Metaphone WWW::BabelfishLANGUAGE & SYNTAX
Error Gives try { } catch { } finally { } Devel::Coverage Devel::Peek Inline Inline::Files Jvm parrot_0.0.6.tgz Perl6::Currying Perl6::Variables Coy Safe::Hole Safe::b2.tar.gz Symbol::Approx::Sub Call methods by approximate names Switch PadWalker Inspect lexicals from enclosing subroutines B::Graph Memoize Softref Sub::Uplevel Alias Like ImplicitThis.pm, but they beat me to the punch =( AnyLoader Automatic module loading on-demand Apache::Mmap mmap interface, not sure about Apache assocation Attribute::Deprecated Attribute::Handlers English::Reference Use words to dereference things - Jerrad Pierce Language::Functional Scalar::Utils List::Utils blessed dualvar isweak readonly reftype tainted weaken Scalar keywords. Weak references, scalar attributes. Array::mode sum(), min(), max() etc methods. overload Package for overloading perl operations - See OverloadOperators package SomeThing; use overload '+' => \&myadd, '::' => \&mysub; # etc ... package main; $a = new SomeThing 57; $b=5+$a; ... if (overload::Overloaded $b) {...} ... $strval = overload::StrVal $b;GRAPHICS
perl::vgalib OpenGL SDL::sdlpl GDGraph3d ANSIColor Curses GD Tk X11::ProtocolIO
Compress::Zlib File::PathConvert IO Load various IO modules IO provides a simple mechanism to load some of the IO modules at one go. Currently this includes: IO::Handle IO::Seekable IO::File IO::Pipe IO::Socket IPC::Open3 open3 - open a process for reading, writing, and error handling $pid = open3(\*WTRFH, \*RDRFH, \*ERRFH, 'some cmd and args', 'optarg', ...); IPC::Open2 open2 - open a process for both reading and writing use IPC::Open2; $pid = open2(\*RDR, \*WTR, 'some cmd and args'); # or $pid = open2(\*RDR, \*WTR, 'some', 'cmd', 'and', 'args'); File::stat By-name interface to Perl's built::in stat() functions use File::stat; $st = stat($file) or die "No $file: $!"; if ( ($st::>mode & 0111) && $st::>nlink > 1) ) { print "$file is executable with lotsa links\n"; } use File::stat qw(:FIELDS); stat($file) or die "No $file: $!"; if ( ($st_mode & 0111) && $st_nlink > 1) ) { print "$file is executable with lotsa links\n"; } File::LockDir File::Path File::SpecSECURITY
IO::Socket::SSL Crypt::SSLeay Crypt::OpenSSL::Random Crypt::OpenSSL::RSA Crypt::OpenSSL::DSANET
Net::DNSServer cnpOa Secure and Extensible Name Server Net::FTP adpf? Interface to File Transfer Protocol Net::AIM adpO? AOL Instant Messenger TOC protocol ARYEH Net::AOLIM RdpO? AOL Instant Messenger OO Interface (TOC) RWAHBY Net::Gnutella bdpO? Gnutella network (v0.4) interface Net::LDAPapi Net::NNTP Net::ICQ bmpO? Client interface to ICQ messaging Net::IMAP adpO? Interface to IMAP Protocol (RFC2060) Net::IRC cdpO? Internet Relay Chat interface Net::Ident RdpO? Performs ident (rfc1413) lookups Net::POP3 adpO? Client interface to POP3 protocol Net::Ping SupOp TCP, UDP, or ICMP ping Net::SFTP bdpOp Secure File Transfer Protocol client Net::SMPP cdpO? Protocol for sending SMS (to GSM or CDMA). SAMPO Net::SMS RdpOp Send SMS wireless text::messages. Net::SMTP adpf? Interface to Simple Mail Transfer Protocol GBARR Net::SNMP MdpOp Object oriented interface to SNMP DTOWN Net::SNPP cdpO? Client interface to SNPP protocol DREDD Net::SOCKS cdcf? TCP/IP access through firewalls using SOCKS SCOOPER Net::SSH Rdphp Perl extension for secure shell IVAN Net::SSL RdcO? Glue that enables LWP to access https URIs Net::ICQV5CD Net::IRC Net::Server Net::Telnet NNTPClient SNMP::Util Socket6 X11::Protocol Math::ematica POP3Client Geo::WeatherNOAA IPTables News::Article Mail::Header Mail::Internet Mail::Mailer Mail::SendmailWEB
CGI_Lite CGI URI URI::Escape Template HTML::Mason HTML::Parser HTML::Template CGI::Application CGI::Carp HTML::Form HTML::TokeParserDATABASE
DBI DBD::JDBC DBD::Pg DBD::Mysql DBD::Oracle DBD::Sybase Sybase::DBlib DB_FileDATA
Storable SOAP cmpO? SOAP/Perl language mapping SWF::File Archive::Any Archive::Tar Pod::DocBook Roman ArrayHashMonster DateManip Mail::MboxParser Tie::CArray XML::Parser XML::QL An XML query language Spreadsheet::ParseExcel Spreadsheet::ParseExcel::Simple MD5 MIME::Base64 MIME::Head Number::Format Crypt::Blowfish Date::Calc Date::Format Date::Parse Date::Simple Digest (and friendes SHA/MD5, etc) Compress::Zlib Archive::Tar Archive::ZipCLASS/OO
Clone idch? Recursive copy of nested objects RDF FreezeThaw bdpf? Convert arbitrary objects to/from strings ILYAZ Persistence:: Class::Object adpO? Store Object definitions with Data::Dumper VIPUL Storable Smcrp Persistent data structure mechanism AMS Marshal::Dispatch cdpO? Convert arbitrary objects to/from strings MUIR Marshal::Packed cdpO? Run::length coded version of Marshal module MUIR Marshal::Eval cdpO? Undo serialization with eval MUIR Tangram RmpO? Object persistence in relational databases JLLEROY Module::Reload Module::Require Module::Use Class::Container Class::Contract Class::Contract Class::Date (high MilliGraham weight) Class::DBI Class::Inner Class::Loader Class::MethodMaker Class::Multimethods Class::ObjectTemplate Class::Observable Class::Privacy Class::PseudoHash Class::Singleton Class::Virtual Class::Visitor Class::Data::Inheritable Class::Delegation Class::Fields Interface::Polymorphism Gives "implements" and "interface" keywords. import use import qw( org::w3c::dom ); # Loads all .pm's in org/w3c/dom/* NEXT Redispatch method lookups Event Base implementation of channel/event listeners and daemon Concurrent::Object See MailOrder - fires off a method call in the background Protect Sub::Parameters - http://search.cpan.org/author/RCLAMP/Sub-Parameters-0.03/lib/Sub/Parameters.pmPERSISTANCE
Persistent::Base bdpO? Persistent base classes (& DBM/File classes) DWINTERS Persistent::DBI bdpO? Persistent abstract class for DBI databases DWINTERS Persistent::MySQL bdpO? Persistent class for MySQL databases DWINTERS Persistent::Oracle bdpO? Persistent class for Oracle databases DWINTERS Persistent::Sybase bdpO? Persistent class for Sybase databases DWINTERS Persistent::mSQL bdpO? Persistent class for mSQL databases DWINTERS Persistent::LDAP bdpO? Persistent class for LDAP directories DWINTERS Persistence::Database::SQL Persistence::Object::Postgres Storable CGI::Persistent CORBA::IOP::IOR adpO? Decode, munge, and re::encode CORBA IORs PHILIPA CORBA::IDLtree Rdpf? IDL to symbol tree translator OMKELLOGGJUST PLAIN FUNNY
Bone::Easy Sex Acme::Inline::PERL Class::Classless - http://search.cpan.org/author/SBURKE/Class-Classless-1.34/Classless.pmUNSORTED
Authen::SASL Bit::Vector (broken) CPAN Devel::Cover Devel::Coverage (broken, but not sure since we got Devel::Cover) Devel::Peek Expect Event FCGI LWP Mon Pod::Man (used by Makefile generated by Makefile.PL) POE PDL (very large, complex and lots of XS) Time::HiRes WxPerl? (lots of XS, very chummy with MakeMaker?)$Id: SelectCPANModules,v 1.19 2003/03/04 11:51:28 phaedrus Exp $
=pod Starts a chunk of documentation. The tags below are then valid. =cut Ends a chunk of documentation, returns to code. =head1 Heading 1st level header. =head2 Heading 2nd level header. =over n Starts an enumerated list indented n spaces. =item Text A bullet item in an enumerated list. =back Ends an enumerated list. =begin FMT Subsequent text up to a matching =end is only included when processed for formatter FMT. =end FMT End enumerated list. =for FMT Following paragraph is included conditionally if formatter FMT is used. BCPAN modules are expected to follow a certain format. pod2man, pod2latex, pod2text, pod2html, and pod2usage take advantage of this format to translate the embedded documentation into other standard formats. In this example, the lines starting with =head1 are litteral and are not changed: the lines between them are.Bold - for command-line switches and program names. C Code E
Escape - Character names from HTML escape sequences, eg lt, gt, amp F Filename I Italics L Link - Cross reference. May be a URL, name of a man page, or the name of =head1, =head2, or =item. May contains optional quotes. A slash may be used to seperate the name of a man page from the section in the man page. S Non-breaking string - will not linewrap on spaces X Index entry.
=head1 NAME Bleah::D::Bleah - Put the actual name of the program there give a sentance about it here =head1 SYNOPSIS ..short usage example =head1 DESCRIPTION ..what it's used for. The body of the document is contained here or in subsections. =head1 EXAMPLES ..how to use it =head1 SEE ALSO ..related works: other modules, programs, RFCs =head1 AUTHORS ..names, email addresses of authorsReference: EffectivePerlProgramming by Joseph N. Hall and Randal L. Schwartz, Addison-Wesley, 1998, pp. 183-187.
<book>, <chapter>, <section>... etc, but does not include extensive style markup. The DocBook source file is processed via tools to produce XHTML, HTML, and through and intermediate form to PDF. This means that one source file can produce both HTML for the Web and also PDF for documents that are printable. If your company produces and/or uses many documents you should consider this technology.
# all the time cvs commit file sends changes to repository cvs update file freshens your files # useful too cvs diff files compare your files to repository cvs log file comments on changes in each file cvs update file merge in changes from the repository cvs update -j currentrev -j previousrev file regress to a previous version # not very often cvs tag string files cvs add files introduce to repository pending commit cvs remove files nuke file from repository cvs checkout file get a file or project initiallyCVS maintains tags in files in the archive, unless a given file is marked "binary". CVS won't add these tags to files, but should they appear, they will be kept up to date as file the checked in and out.
$Id: CvsQuickRef,v 1.5 2003/01/24 16:08:41 httpd Exp $ $Author: httpd $ $Revision: 1.5 $The entire tag does not need to be inserted into the file; only the beginning dollar sign, the tag name, the color, a space, then the ending dollar sign. Since this document itself is kept in CVS, these tags have been coopted.
(my $version) = '$Revision: 1.5 $' =~ /([\d\.]+)/;XXX the following information needs to be merged in to this explaination, and I need to devise a solution to the MakeMaker problem.
If you want to release a 'beta' or 'alpha' version of a module but don't want CPAN.pm to list it as most recent use an '_' after the regular version number followed by at least 2 digits, eg. 1.20_01. If you do this, the following idiom is recommended: $VERSION = "1.12_01"; $XS_VERSION = $VERSION; # only needed if you have XS code $VERSION = eval $VERSION; With that trick MakeMaker will only read the first line and thus read the underscore, while the perl interpreter will evaluate the $VERSION and convert the string into a number. Later operations that treat $VERSION as a number will then be able to do so without provoking a warning about $VERSION not being a number.What actually gets put into files by h2xs:
our $VERSION = '0.02'; $VERSION = eval $VERSION; # seeSee Also: CvsIntro
http://www.pobox.com/~schwern/cgi-bin/perl-qa-wiki.cgi?ModuleNamingSee Also: C2 DontNameClassesObjectManagerHandlerOrData - touches on many design ideas in conjuction
[1] Notes on Synthesis of Form - Christopher Alexander, Ale 1964 [2] A Pattern Language - Christopher Alexander, Ale, 1977 ISBN 0-19-501919-9 [3] Design Patterns: Elements of Reusable Object Oriented Code, Gang of Four, XXX C2 DesignPatternsElementsOfReusableObjectOrientedSoftware DesignPatternsBook by the GangOfFour [4] Refactoring: Improving the Design of Existing Code, XXX MartinFowler's book, ISBN 0201485672 . C2 RefactoringImprovingtheDesignofExistingCode [5] Ward et el. http://c2.com/cgi/wiki/ C2 FunctionalPatternSystemForObjectOrientedDesign [6] Bitter Java ISBN 1-930110-43-X (not my favorite) [7] Applied Java Patterns ISBN 0-13-093538-7 (very good) [8] Java Threads, O'Reilly (very good) [9] Joy of Patterns - Brandon Goldfedder - ISBN 0-201-65759-7 (good) [10] "Object Oriented Perl", Damian Conway (good) [11] Advanced Algorithms in Perl [12] Structure and Interpretation of Computer Programs by Harold Abelson, Gerald Jay Sussman and Julie Sussman (very good) C2 StructureAndInterpretationOfComputerPrograms [13] perlmodlib [14] perlnewmod [15] http://www.cpan.org/modules/00modlist.long.html [16] Anti-Patterns, ISBN 471197130 [17] Elements of Programming with Perl, ISBN 1-884777-80-5, Andrew Johnson [18] Practice of Programming, Brian W. Kernighan, Rob Pike, ISBN 0-201-61586-X [19] Object Oriented Design Heuristics, Arthur J. Riel, Prentice Hall, ISBN 0-201-63385-X [20] The Mythical Man Month, Frederick Brooks C2 TheMythicalManMonth - the greatest project management book ever written [21] Pattern Language of Program Design 4 [22] 6.170 Software Laboratory, MIT Open Courseware: http://ocw.mit.edu/6/6.170/f01/lecture-notes/index.html http://mitpress.mit.edu/sicp/full-text/book/book.html - available online [23] Zen and the Art of Motorcycle Maintenance C2 ZenAndTheArtOfMotorcycleMaintenance - ideas on quality as the essense of existance [24] StructuredProgramming, Dahl, Dijkstra, TonyHoare, Academic Press, 1972$Id: ReferencesMeta,v 1.12 2003/03/04 09:32:50 phaedrus Exp $
$a = wussup;Barewords are unquoted strings other than function names. They get treated as strings for lack of anything else to do with them. That makes that expression the same as:
$a = "wussup";Not quoting string literals is considered bad style in all but one case: hash table indices. Other uses are dissallowed when UseStrict is in effect.
# this is a comment $a = 10; # and so is thisBlock quotes are attainable using the POD syntax documented in PerlDoc. Comments quoted this way will become part of the documentation of the module or program, much like how javadoc processes comments into documentation. It is advisable to use this syntax before functions, perticularly publicly accessable ones, to explain what the function does, why, and what it requires to do so.
# CGI.pm does this to learn about local linefeed insanity: $OS = $Config::Config{'osname'};Environment variables are included, of course. They are contained in the Perl hash, %ENV. For example:
print "Search path: ", $ENV{PATH}, "\n";Functions Without Parentheses
my @files = grep { $_ !~ m/^\./ } sort readdir $fh;This might lead one to believe that parentheses aren't used for function arguments and curly braces some how take their place. This might be partially true. Many functions expect a list of things, and many functions return lists of things. In this example, readdir() is returning a list to sort(); sort() is sorting that and sending it through grep(); grep has a strange little block after it. This is a block of code that will be run each item in the list as a test for inclusion. sort() and map() are two other functions that accept such an code block. The block is purely optional. Since all of our lists are implicitly created and implicitly passed around, we don't ever really need to group things using parenthesis. That doesn't mean we can't:
my @files = get_file_list($ENV{'HOME'}, '.', `pwd`);Having optional parentheses does create a certain amount of confusion. People are often tempted to write expressions like:
print (32/8)+10; # this prints "4"The space after print() means nothing. It still attaches itself to the single argument, 32/8. What probably meant is:
print((32/8)+10); # this prints "14"The && operator also causes problems when parenthesis aren't used on lists of numbers. The && operating will work on the last thing in the list, rather than the return value of the function:
sprintf "The number is: %d\n", 0 && return; # this won't returnThe obvious thing to do is introduce parenthesis, but you don't have to - Perl provides the and and or operators, which have very low precedence:
sprintf "The number is: %d\n", 0 and return; # this will returnReadability can suffer, but not having to match up parentheses all of the time saves a lot of bouncing around in the code. In case of doubt, use the parentheses.
substr($foo, $i, 1);This will tell Perl that we want a string of 1 element starting at the $ith element of $foo. Since strings aren't arrays of characters, this returns a string of one character. Use ord() and chr() to convert between ASCII values and strings of one character.
*pi = \3.14159; # I'm not a math geekThis uses typeglobs, an obscure feature of Perl. Typeglobs define file-global variables by holding references to scalars, hashes, arrays, and so forth. We're storing a reference to a number directly, rather than a variable. This prevents accidential modification later on.
You can also read in Perl code that defines variables using the require statement.
@array = (1, 2, 3, 'hi');However, arrays that aren't passed by reference get copied. That means:
function(9, 10, @array);...//function()// shouldn't expect to get a reference to @array. Instead, everything inside of array will be copied onto the end of function()'s argument list, making it equivilent to saying:
function(9, 10, 1, 2, 3, 'hi');Since so many of Perl's built in functions work on plain old flat lists, and it is easy to compose lists from many sources to feed to a function, this is officially considered a feature.
use warnings; my $sum = 0; my @prices = (10.95, 15.95, 5.95, 25.95, 45.95); foreach my $price (@prices) { $sum += $price; } print "$sum was yesterdays price. It isn't valid anymore. Sorry! Offer expired!\n"; $sum = undef; print "The total for this order is: $sum\n"; # this gives an error messageThe text of the error was "Use of uninitialized value in concatenation (.) or string at - line 9.".
if(undef == 0) { print "True\n"; } my $a = undef; if($a == 0) { print "True\n"; }Logically, undef == 0. Many built in functions return a number on success and undef on failure: 0 is a possible success return value. So is the null string, ''. Rather than testing for truth, you must test for definidness:
if(defined my $line = <>) { # deal with more frivelous user demands } # user has shut up now and hit Control-D or closed the connectiondefined() returns true if its argument is defined and false if its argument is in fact not.
do { my $a = [1, 2, 3]; my $b = [4, 5, 6]; $a->[4] = $b; $b->[4] = $a; # uh oh! } # $a and $b still live on, even though they are out of scopeYou might not notice if you accidently do a few of these babies in code that runs only briefly then exits, like a CGI script. This can leak memory (allocate memory never to be freed) and ultimately expend all of the available resources on a long-running application, like a daemon. See DaemonProcess. ScalarUtils defines a weaken() function for creating weak references - an effective way of avoiding leaks.
int stuff[] = {1, 2, 3, 4}; /* an array in C, by reference */ my $stuff = [1, 2, 3, 4]; # an array reference in Perl my @stuff = (1, 2, 3, 4); # an array in PerlOn the second line, the [47] constructs a reference to a list, which then gets stored in $stuff. $stuff itself is not a reference, but merely a SCALAR that has an array reference stored into it. Remember, there is no pointer/reference type in Perl.
@ar = map { $_++} @ar; foreach my $i (@ar) { $i++ } # optionally, for means the same thing as foreachFor performance, Perl programmers sometimes tell Perl to pre-grow arrays, instead of letting Perl do it on demand:
@ar = 1000; # allocate 1000 slotsArrays can be multidimentional. C arrays allocated thus:
char message[10][255]; /* 10 messages of 255 characters each */...are entirely preallocated, and have no lookup table: since each message is of fixed size, C need only look for the first one at offset 0, the next one at offset 255, the next at offset 510, and so on. This is not how Perl arrays work. Arrays allocated like:
char *message[]; /* I don't know how many messages or how long it might be */...is a much a much closer faximile. A pointer holds the address that points to a row of characters in memory. In the same way, a Perl array can list references to other arrays. The -> operator dereferences reference types of various type. This example creates a reference (pointer) to an array, and starts populating it with references to other arrays:
my $arrayRef = []; $arrayRef->[0] = [1, 2, 3]; $arrayRef->[1] = [4, 5, 6]; $arrayRef->[2] = [7, 8, 9]; print $arrayRef->[1]->[1], "\n"; # prints 5, right in the middle, just like Hollywood SquaresBecause it is unambigious, Perl accepts as a short hand this syntax for indicing multidimentional arrays:
print $arrayRef->[1][1], "\n"; # like aboveNote that the first -> is still required to distinguish between the reference $arrayRef and the array @arrayRef. In some languages, arrays are objects. Perl is not one of them. In other languages, arrays are never objects. Perl is not one of them either. In Perl, arrays are sometimes objects. When they are objects, they do not a provide a standard, generic API for minipulating arrays, but rather provide an API cooked up off the cuff by the creator of the object. This means that the API is more likely to be useful but less likely to be consistent.
gt lt eq ne cmp # like >, <, ==, !=, <=>, but for strings: compares lexically or literally # this prints true - you probably want the "eq" operator: if("hello" == "world") { print "true\n"; } -> # dereference any reference type. the exact syntax depends on the reference: $a->[0] # dereference an array ref $a->{'hi'} # dereference a hash ref $a->blurgh() # dereference an object referenceIt is not uncommon to mix and match them in one statement, if you know the datatypes of everything:
$a->[0]->{'hi'}->blurgh();If you have trouble with this, rethink what returns what reference types. This assumes that you have a scalar, $a, that is an array reference. That array holds a list of references to hashes (hash references). Those hashes each contain references to objects. Actually, that is too broad of a statement - Perl is dynamically typed, but this paragraph assumes that you've been consistent with your datastructure.
if($foo == 1) { # do something } elsif($foo == 2) { # do something else } else { # complain and do nothing }Forward References
$color = $obj->getColor();Fetching data from a reference to a subroutine:
$color = $colorSubroutine->();Fetching data from a reference to a hash:
$color = $stuff->{'color'};Fetching data from an array reference:
$color = $colors->[37];Fetching data from a sca lar reference:
$color = $$colorPointer;Fetching data from a reference to a filehandle:
$color = <$colorFile>;It's up to you to use a reference the correct way. It may help to use paper to make a graph of what contains what. If you find things empty that you know you stored data in, or perl is telling you you can't use one reference type as another, you've gotten confused at some point. Perl 5 references are second only to regular expressions in creating "line noise" code. It's possible to have an array reference which contains hash references which contain object references. The secret is to remember which contains what, and request them in the correct order:
$color = $anArray->[42]->{'thatOne'}->getColor();Often, calling a method in an object will return a new object. Sometimes you'll find yourself using, but not saving, intermediate objects:
$color = $outside->getSky()->getColor();"Programming Perl", O'Reilly Press, offers a complete rundown on using references, creating them, creating objects, and more. It is a thourogh tour of the how and why of Perl. That knowledge isn't critical to understand this book, but on the other hand, we don't replicate that information here, either.
package Man; sub new { my $type = shift; my $me = { }; bless $me, $type; } return 1;An object is an instance of a class. If you're a biology major, an object is a phenotype and a class is a genotype. A class is a prototype, and an object came off of an assembly line. For Perl purposes, "class", "package", and "type" are interchangeable. A package is the prototype for making objects. It gives the instructions on how to make a new object, and it defines how it will work once its made. While the package is the form a class takes, the variable that you bless is the core of the object. Two things happen: bless() mucks with the variable, recording the name of the package that it is now part of, so attempts to call methods in it in the future will be routed back to the package it was blessed in. Also, it contains all of the information specific to particular instance of the class. "Instance" is OO jargon that translates to "an object made from a certain package". $me is assigned to { }, which when used as a value creates a reference to a hash. Thus, $me becomes a hash reference. The basic datatype our object will store data in is going to be a hash, in this case. My variables are intimately tied to objects in Perl, since the package that implements the objects has to handle each and every object it creates, and not confuse their data with each other. My variables are not only private to particular method they are defined in, but they aren't even visible from outside that method. We'll encounter more useful magic of the "my" variable later. In OO lingo, the function in a package that sets up a new object, typically named new(), is called the "constructor".
use Man; $obj = Man->new();This fragment calls the new() method in the Man (if it isn't defined, inheritance kicks in and new() is searched for). Note that 'Man' isn't actually an object reference. Its just a bareword. Perl takes it as a class name when used like this. Another way to say the same thing, which resembles other OO languages more closely, is:
use Man; $obj = new Man;In either case, the string literal 'Man' is passed to the new() method. This is important, since the new() method doesn't always know what kind of object its making. This happens when one package inherits another. If the new() made assumptions about what kind of objects it was making, it couldn't be trusted to help make objects for a subtype of itself. The creating of the object is done by the perl built-in bless. The "use" statement tells Perl to seek out the module and load it in. In order to call new() in it, we're going to need to have it already loaded.
package InvestmentBanker; @ISA = ('Man'); sub getTimeOfDay { my $me = shift; unless(caller->isa(__PACKAGE__)) { die "Not for you, buddy!"; } return scalar localtime(time()); } return 1;When we create an InvestmentBanker, The "$me" that we get was actually created in Man. Since InvestmentBanker doesn't define a new() method, Perl examines the @ISA array to decide what else we are. Our more primitive being may know how to cope with this situation. In this case, new() is called in Man with the argument 'InvestmentBanker'. It happily returns one. When parents give birth to investment bankers, they usually don't know right away. They do know that they are giving birth to a person. PACKAGE__ can always be used to retrieve the name of the current package, which is "main" by default, or whatever you've set it to using the "package" statement. You could use a string here that is set to the same thing that you set the package to, but this lets you set it one place and never have to spell it correctly again. The caller() method examines the caller stack. The caller stack keeps track of where we are in the program, especially where we need to return to as each function or method finishes. In this case, we're concerned with the object that called our method. In scalar context, caller() returns a package name. We're using that package name as if it were an object, just like we did above with Man->new(). This doesn't invoke a specific instance of the package, but rather goes directly to the package. isa() is a method defined in the UNIVERSAL package that searches through the inheritance tree for you, as defined by which classes list which other classes in their @ISA arrays. In this example, we want to know if the class that called us has an "is a" relationship (that is, either actually is, or inherits from) our class, InvestmentBanker.
use InvestmentBanker; my $banker = new InvestmentBanker; print $banker->getTimeOfDay();If you run this code from a separate file, you'll get a message similar to:
use InvestmentBanker; package WallStreetTrader; @ISA = ('InvestmentBanker'); my $banker = new InvestmentBanker; print $banker->getTimeOfDay();Then, we get a more friendly answer. Investment Bankers aren't bad people, they're just very, very busy. Sometimes you have to be careful with whom you speak, for reasons of security. This behavior, where we consider the package of the code calling us, is similar to the behavior of the Java "protected" keyword. This is part of the idea of "encapsulation", and becomes extremely useful when you want your own packages to have special access to other instances of themselves. This happens when you need to make your packages work closely together in ways aren't safe for other people to assume they can, such as in ways that you might change in the future, or ways that would by-pass the OO nature of your object if just anyone could use it. It should be noted that some languages check your program at compile time, before it runs, to make sure you aren't trying to access something protected in another class. Perl 5 doesn't.
package TradingFloor; sub new { my $type = shift; my $me = {}; bless $me, $type; } sub play { my $me = shift; my $investor = shift; $investor->isa('InvestmentBanker') or die; my $stock = shift; $stock->isa('Stock') or die; my $amount = shift; $stock->set_quantity($amount); $me->{$investor}={$stock}; } package Stock; sub new { my $type = shift; my $me = {@_}; bless $me, type; } foreach my $i (qw(name price quantity)) { my $field = $i; *{"get_$field"} = sub { my $me = shift; return $me->{$field}; }; *{"set_$field"} = sub { my $me = shift; @_ or die "not enough arguments to set_$field, stopped"; $me->{$field} = shift; return 1; }; }Put Stock in a file named Stock.pm and TradingFloor in a file named TradingFloor.pm. Then, in a separate file, run this code:
use WallStreetTrader; use TradingFloor; use Stock; $trader = new WallStreetTrader; $stock = new Stock(name=>'ILL', price=>5.45); $wallstreet = new TradingFloor; $wallstreet->play($trader, $stock, 10000);The play() method in TradingFloor.pm accepts a reference to an object made from itself, as its first argument. This is typical of methods. The package has the code, but the object has the data. We have to look inside of the object ($me) to get at the data of the object. Other OO languages implicitly take this first argument, often called "this". Perl 5 requires you to manually accept this argument. That isn't all, though. The method is looking for two more arguments: an InvestmentBanker object, and a Stock object. We ask the values we receive if they are of the type we expect (or if they inherit from those types). This is called "type safety" in OO lingo. In Perl, we process our arguments manually, and we enforce type safety manually. Thankfully, its rather painless. Should we receive something other than an InvestmentBanker or a Stock, we complain immediately and loudly. Some languages check this when the code is compiled, before the program is ever run. Perl 5 does so at runtime.
sub get_name { my $me = shift; return $me->{'name'}; }; sub set_name { my $me = shift; @_ or die "not enough arguments to set_name, stopped"; $me->{'name'} = shift; return 1; };Stock.pm is an example of data encapsulation, and acts as nothing more than a simple container for information. Future version of Stock.pm could guard data closely, or perform other operations on it. Because the accessors are code, instead of just variables, it gives us the precious chance to do some fudging: for instance, if the name of the Stock were removed, to be replaced with 'companyName' and 'tickerSymbol', we could redefine get_name() to return the tickerSymbol and companyName, combined into one string. set_name() could perhaps change companyName, but call warn() to warn the programmer that a legacy method was being called, and that she needs to use an updated accessor now.
use base 'Person';This uses a "pragma" module to do the same thing as assign to @ISA, but with a cleaner syntax.
::::, at its most basic level, tells Perl where to look for a module:
http://patternsinperl.com/ - NigelWetters' less complete but more polished site http://hatena.dyndns.org/~jkondo/DesignPattern/ - less talk, more code! http://magnonel.guild.net/~schwern/talks/Design_Patterns/full_slides/ http://magnonel.guild.net/~schwern/talks/Refactoring/slides/My (Yet Incomplete) List of References:
ReferencesMetaDesignPatterns Yet to Be Merged:
C2 TransactionsAndAccounts C2 InfocomZork - the natural language parser is brilliant due to a simple idea C2 TellDontAsk - related to AccumulateAndFire hereAntiPatterns:
C2 EveryNightAtMidnight - polling without wait/notify is inheriently problematic C2 ArrowAntiPattern - nested ifs and whiles should refactor to objects and methods respectively C2 BackPedalling - having to recreate state: symptom of insufficient state C2 VoodooChickenCoding - symptoms of desperation or paranoia http://www.jwz.org/doc/java.html - Why Java sucks - we've overcome many of these but good lessons to learn! http://www.rahul.net/kenton/40errs.html - 40 most common XWindows programming mistakesOther DesignPatterns Stuff:
http://ask.slashdot.org/article.pl?sid=02/10/15/2352256&mode=thread&tid=156 - UI Design Patterns http://www.samag.com/documents/s=1280/sam02010001/ - Program Like Ya Mean It: TPJ http://perlmonks.org/?node_id=193340 - Are Design Patterns Worth It? http://perlmonks.org/index.pl?node_id=133399 - Design Patterns Considered Harmful http://perl.plover.com/yak/design/ - "Design Patterns" Aren't - Dominus http://www.norvig.com/design-patterns/ - Design Patterns in Dynamic Languages http://www.laputan.org/foote/papers.html - BrainFoote, author of BigBallOfMud pattern http://industriallogic.com/papers/learning.html - a learning guide to the GoF bookObjectOriented Stuff:
http://www.advogato.org/article/635.html - Why UML and OpenSource don't mix - UnifiedMarkupLanguage http://www.stanford.edu/~engler/p401-xie.pdf - Redundant code sign of larger problems http://martinfowler.com/books.html - MartinFowler, http://martinfowler.com/ http://www.advogato.org/article/575.html - Framework Building Rules of Thumb http://www.iarchitect.com/mshame.htm - Interface Hall of Shame http://mpt.phrasewise.com/stories/storyReader$374 - When good interfaces go crufty http://www.bell-labs.com/cgi-user/OrgPatterns/OrgPatterns?WebIndex - Organisational (people) Patterns Wiki http://java.sun.com/features/2003/01/lanier_qa1.html - Programs beyond 10 million lines http://linux.oreillynet.com/pub/a/linux/2001/05/04/xp_intro.html - ExtremeProgramming intro http://www.construx.com/seminars/onsite/topic-intro-oo.htm -- Good OO outline http://java.sun.com/people/jag/Presentations/TheStoryOfJava/img20.htm - Java's Design PrinciplesBooks:
http://savannah.nongnu.org/projects/latex-manual/ - Free LaTeX manual http://savannah.nongnu.org/projects/style-guide/ - Programming Style Guide http://savannah.gnu.org/projects/wcpp-book/ - Webclient Programming In Perl, Free Book http://savannah.nongnu.org/projects/pup/ - Picking Up Perl, Free Book ISBN 0596003102 - O'Reilly's ComputerScienceAndPerlProgramming - select TPJ articles http://www.oreilly.com/catalog/tpj1/ - on oreilly.com - looks interesting! ISBN 0-596-00206-8 - Programming WebServices with Perl http://www.oreilly.com/catalog/pwebserperl/ ISBN 1571690581 - PERL 5 how-to: The Definitive PERL Programming Problem-Solver Reggie David Ed Weiss Mike Glover - Sams Publishing - Any good? I don't know! ISBN 0137613059 - Advanced Topics in PERL - Pren Hall DataMiningCookbook http://www.masonbook.com/ - Embedding Perl in HTML with MasonRefactoring:
C2 WikiPagesAboutRefactoringRandom Perl Links:
http://www.perlmonks.org - Perl Monks http://www.perldoc.com/ http://www.perldoc.com/perl5.8.0/pod.html - all sorts of documentation for Perl http://conferences.oreillynet.com/pub/w/15/presentations.html - Perl Presentations http://perl.about.com/cs/intermediateperl/ - http://about.com 's Intermediate Perl Area http://perl.about.com/cs/advancedperl/ - http://about.com 's Advanced Perl Area http://perl.about.com/library/glossary/blglossary.htm - http://about.com 's Perl Glossary http://www.pobox.com/~schwern/talks/ http://www.pobox.com/~schwern/cgi-bin/perl-qa-wiki.cgi - MichaelSchwern's Perl Wiki! http://www.perl.com/pub/a/language/versus/perl.html - Perl Gotchas http://www.perl.com/language/style/slide-index.html - Perl Style http://www.perl.com/tchrist/defop/defconfaq.html - Truth vs Definedness http://www.perl.com/language/style/slide-index.html - Perl Style http://perl.plover.com/IAQ/IAQlist.html - Infrequently Asked Questions http://www.perl.com/doc/FMTEYEWTK/index.html - Far More Than You Ever Wanted To Know - 1996 =) http://builder.com.com/article.jhtml?id=u00320021008BR101.htm&fromtm=e601-2 - Soap article http://www.perl.com/pub/a/2002/11/14/exception.html - OO style exception handling http://perl.plover.com/FAQs/Namespaces.html - Coping with Scoping OOops, lost the link - whats new in 5.8 http://www.oreillynet.com/pub/a/oreilly/perl/2002/11/04/perlsuccess.html - Perl Success Stories http://www.onlamp.com/pub/a/onlamp/2002/01/17/slash_plugin.html - slash wiki plugin http://milwaukee.pm.org/pub/Milwpm/PerlAquarium/raindrop.tar.gz - Perl Fishtank http://www-106.ibm.com/developerworks/linux/library/l-genperl/?t=gr,lnxw01=PerlGenetics Genetic Algorithms in Perl http://www.riehle.org/vuw-mirror/CS-TR-02-9.pdf - Postmodern Programming http://www.devx.com/dotnet/articles/ym81502/ym81502-1.asp - Perl & .NET Interop via Interfaces http://documentation.perl.org/ - Perl Documentation Project http://perlmonks.org/index.pl?node=The%20St.%20Larry%20Wall%20Shrine - Larry Wall shrine http://www.perlmonth.com/ - Defunct, odd Perl magazinePerl XS, assembly, internals:
http://www.swig.org http://www.perlmonth.com/perlmonth/issue6/modules.html http://www.perlmonth.com/perlmonth/issue7/modules.html http://www.perlmonth.com/perlmonth/issue8/modules.html http://www.perlmonth.com/perlmonth/issue9/modules.html http://www.perlmonth.com/perlmonth/issue10/modules.html PerlAssemblyBackground and cool CSy information:
C2 GreatProgramsToRead C2 WhenToUseInnerClasses - alternative to multiple inherits, too many interfaces C2 DontNameClassesObjectManagerHandlerOrData - touches on many design ideas in conjuction C2 DualityBetweenStateAndClass - given kinds of things, at which point to make subclasses? C2 BizarreLoveTriangleInJava - solution looks like multiple inheritance, tho the InfocomZork parser certainly cuts it http://www.cs.rit.edu/~afb/20013/plc/slides/procedures-07.html - Jensen's Machine - local value pools http://www.cs.rit.edu/~afb/20013/plc/slides/procedures.html - Wow - Language Cencepts http://java.sun.com/people/jag/pathfinder.html - Prioty Inversion with Thread Deadlocks http://www.htdp.org/2002-05-09/Book/curriculum-Z-H-1.html - How to Design Programs http://www.cs.indiana.edu/eopl/ - Essentials of Programming Languages http://slashdot.org/article.pl?sid=02/08/28/1655207 - Literate programming http://www.cs.uu.nl/people/franka/ref - Programming Language Theory Texts - Excellent! http://www.slowass.net/phaedrus/todospool.pl?mesgid=29 - NeverRewriteSoftware http://www.bagley.org/~doug/shootout/ - Language Shootout - Awesome! http://icfpcontest.cse.ogi.edu/ - Functional Programming Competition - all languages welcome! http://www.gnu.org/prep/standards_toc.html - GNU Coding Standards http://www.advogato.org/article/539.html - Long term programming techniques http://www.csd.uwo.ca/gradstudents/chicha/aa.html - Algorithm Animations http://www.owasp.org/guide/ - Web Applications Security Paper http://www.osnews.com/story.php?news_id=1901&page=1 - Fred Brooks references http://ask.slashdot.org/askslashdot/02/10/26/2223230.shtml?tid=156 - Programming Disasters http://ocw.mit.edu/6/6.170/f01/index.html - MIT Open Courseware - Software Engineering Lab http://www.assembly.org/ - Demo coding! Yay! http://www.joelonsoftware.com/index.html - JoelOnSoftware - insights, usability http://www.atarimagazines.com/ - Lots of great old magazine articles with code http://www.gnutella2.com/gnutella2_search.htm - Gnutella 2 Protocol Proposal - Kind of shunned by community http://java.sun.com/features/2002/11/gabriel_qa.html - The Poetry of Programming http://www.creativecommons.org/ http://grotto11.com/blog/slash.html?+1039831658 http://theory.whirlycott.com/~phil/antispam/rbl-bad/rbl-bad.html - The Spam Problem: Moving Beyond RBLs http://www.oreillynet.com/pub/a/network/2002/10/21/community.html - online communities http://martinfowler.com/ articles/evodb.html - Evolving Databases http://www.wired.com/wired/archive/11.02/code.html - Immortal Code http://developer.kde.org/~sewardj/ - ValGrind http://www.rubycentral.com/book/ - Programming Ruby - The Pragmatic Programmer's Guide http://levine.sscnet.ucla.edu/ - Game Theory & Economics JoelOnSoftwareBlogs:
http://www.wall.org/~larry/perl.html - LarryWall 's Perl page http://www.perl.com/pub/a/language/admin/whats_new.html - TomChristiansen 's blog http://use.perl.org/~brian_d_foy/journal/ - brian d foy's blog http://www.oreillynet.com/pub/au/176 - chromaticPerl Newbie Stuff Other Than Books:
http://perlmonks.org/index.pl?node_id=216602 - Perl quickrefRelated email to/from me:
My initial post to PheonixPerlMongers list: http://www.slowass.net/phaedrus/todospool.pl?mesgid=104 Perl with Meta-Lists a la Lisp http://www.slowass.net/phaedrus/todospool.pl?mesgid=42 Conversation with JB: http://www.slowass.net/phaedrus/todospool.pl?mesgid=75 http://www.slowass.net/phaedrus/todospool.pl?mesgid=76 http://www.slowass.net/phaedrus/todospool.pl?mesgid=77 SecondSystemSyndrome care of PerlSixInternals: http://www.slowass.net/phaedrus/todospool.pl?mesgid=63 Many assorted thoughts on things: http://www.slowass.net/phaedrus/todospool.pl?mesgid=41 PerlDesignPatterns considered failure: http://www.advogato.org/article/579.htmlSpeaking and Publishing Tips:
http://www.advogato.org/article/549.html - How to get a conference topic accepted http://perens.com/Books/ - BrucePrens and PrenticeHallOther cookbooks and lexicons of note:
http://www.lileks.com/institute/gallery/spec.html - Regretable Food NewAnarchyCookbook http://sunsite.berkeley.edu/Literature/Bierce/DevilsDictionary/ http://wombat.doc.ic.ac.uk/foldoc/See also: JavaResources, ForthResources, PerlNewbie
Copyright (C) 2000 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.0. PREAMBLE
from that of the Document, and from those of previous versions (which should, if there were any, be listed in the History section of the Document). You may use the same title as a previous version if the original publisher of that version gives permission.B. List on the Title Page, as authors, one or more persons or entities
responsible for authorship of the modifications in the Modified Version, together with at least five of the principal authors of the Document (all of its principal authors, if it has less than five).C. State on the Title page the name of the publisher of the
Modified Version, as the publisher.D. Preserve all the copyright notices of the Document. E. Add an appropriate copyright notice for your modifications
adjacent to the other copyright notices.F. Include, immediately after the copyright notices, a license notice
giving the public permission to use the Modified Version under the terms of this License, in the form shown in the Addendum below.G. Preserve in that license notice the full lists of Invariant Sections
and required Cover Texts given in the Document's license notice.H. Include an unaltered copy of this License. I. Preserve the section entitled "History", and its title, and add to
it an item stating at least the title, year, new authors, and publisher of the Modified Version as given on the Title Page. If there is no section entitled "History" in the Document, create one stating the title, year, authors, and publisher of the Document as given on its Title Page, then add an item describing the Modified Version as stated in the previous sentence.J. Preserve the network location, if any, given in the Document for
public access to a Transparent copy of the Document, and likewise the network locations given in the Document for previous versions it was based on. These may be placed in the "History" section. You may omit a network location for a work that was published at least four years before the Document itself, or if the original publisher of the version it refers to gives permission.K. In any section entitled "Acknowledgements" or "Dedications",
preserve the section's title, and preserve in the section all the substance and tone of each of the contributor acknowledgements and/or dedications given therein.L. Preserve all the Invariant Sections of the Document,
unaltered in their text and in their titles. Section numbers or the equivalent are not considered part of the section titles.M. Delete any section entitled "Endorsements". Such a section
may not be included in the Modified Version.N. Do not retitle any existing section as "Endorsements"
or to conflict in title with any Invariant Section.If the Modified Version includes new front-matter sections or appendices that qualify as Secondary Sections and contain no material copied from the Document, you may at your option designate some or all of these sections as invariant. To do this, add their titles to the list of Invariant Sections in the Modified Version's license notice. These titles must be distinct from any other section titles.
Copyright (c) YEAR YOUR NAME. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with the Invariant Sections being LIST THEIR TITLES, with the Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. A copy of the license is included in the section entitled "GNU Free Documentation License".If you have no Invariant Sections, write "with no Invariant Sections" instead of saying which ones are invariant. If you have no Front-Cover Texts, write "no Front-Cover Texts" instead of "Front-Cover Texts being LIST"; likewise for Back-Cover Texts.