package Pointy::Counter;

use 5.006;
use strict;

our (@EXPORT, @ISA);
BEGIN
{
	require Exporter;
	@ISA    = qw/Exporter/;
	@EXPORT = qw/counter/;
	
	$Pointy::Counter::AUTHORITY = 'cpan:TOBYINK';
	$Pointy::Counter::VERSION   = '0.001';
	
	eval { require Object::AUTHORITY and import Object::AUTHORITY };
}

use overload 
	'--' => sub { my ($self) = @_; $$self++ },
	'>'  => sub { my ($self, $other) = @_; $$self < $other },
	'""' => sub { my ($self) = @_; $$self },
	'+0' => sub { my ($self) = @_; $$self },
	fallback => 1,
	;

sub new
{
	my ($class, $initial) = @_;
	$initial = 0 unless defined $initial;
	bless \$initial, $class;
}

sub counter
{
	__PACKAGE__->new(@_);
}

sub continue
{
	my ($self) = @_;
	--$$self;
}

sub value :lvalue
{
	my ($self) = shift;
	$$self = shift if scalar @_;
	$$self;
}

__FILE__
__END__

=head1 NAME

Pointy::Counter - funny syntax for loops

=head1 SYNOPSIS

  use Pointy::Counter;

  my $i = counter;
  while ($i --> 10)
  {
    say "\$i is $i";
  }
  
  # says $i is 1
  # says $i is 2
  # ...
  # says $i is 10

=head1 DESCRIPTION

Pointy::Counter is a class that provides objects which seem to act like
numbers, but have a special C<< --> >> operator to count up to a particular
value.

OK, confession time... C<< --> >> is not really an operator. It's a
post-increment followed by a greater than sign.

  $i --> 10

is parsed by Perl like:

  ($i--) > 10

Then the Pointy::Counter class overloads C<< -- >> to increment rather than
decrement, and overloads C<< > >> to act as a less-than. If you try to
perform any other maths, it should just act as a normal scalar. In
particular, note that this means that while C<< $i-- >> will do a counter
increment; C<< $i -= 1 >> will act completely differently, decrementing the
counter and restoring it to a normal Perl scalar.

=head2 Constructor

=over

=item C<< Pointy::Counter->new($initial) >>

Creates a new counter, with the initial value (defaults to 0). Note that
the counter will have value C<< $initial >> before the loop starts, but
within the body of the loop, it will be C<< $initial+1 >>, C<< $initial+2 >>,
etc.

=item C<< counter $initial >>

This module exports a function which can be called as a shortcut for the
constructor.

=back

=head2 Methods

=over

=item C<< value >>

Returns current value as a plain old Perl scalar. This is an lvalue
subroutine, so you can, for example, reset a counter using:

 $i->value = 0;

=item C<< continue >>

Really does decrement the counter. This is used to solve a small niggling
problem:

 my $x = counter;	
 while ($x --> 2)
 {
   say "Counter is $x (loop A)";
 }
 while ($x --> 4)
 {
   say "Counter is $x (loop B)";
 }

Will output:

 Counter is 1 (loop A)
 Counter is 2 (loop A)
 Counter is 4 (loop B)

Why doesn't it output a line for when its value is 3? That's because it only
takes the value 3 I<between> the two loops. The solution is to decrement the
counter before starting loop B:

 my $x = counter;	
 while ($x --> 2)
 {
   say "Counter is $x (loop A)";
 }
 $x -> continue;
 while ($x --> 4)
 {
   say "Counter is $x (loop B)";
 }

This gives you:

 Counter is 1 (loop A)
 Counter is 2 (loop A)
 Counter is 3 (loop B)
 Counter is 4 (loop B)

=back

=head1 BUGS

Please report any bugs to
L<http://rt.cpan.org/Dist/Display.html?Queue=Pointy-Counter>.

=head1 SEE ALSO

L<overload>, L<perlsyn>.

=head1 AUTHOR

Toby Inkster E<lt>tobyink@cpan.orgE<gt>.

=head1 COPYRIGHT AND LICENCE

This software is copyright (c) 2011 by Toby Inkster.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=head1 DISCLAIMER OF WARRANTIES

THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.

