=head1 NAME

Params::Classify - argument type classification

=head1 SYNOPSIS

	use Params::Classify qw(scalar_class
		is_undef
		is_string is_number
		is_glob
		is_ref ref_type
		is_blessed blessed_class
		is_strictly_blessed is_able);

	$c = scalar_class($foo);

	$ok = is_undef($foo);

	$ok = is_string($foo);
	$ok = is_number($foo);

	$ok = is_glob($foo);

	$ok = is_ref($foo);
	$t = ref_type($foo);
	$ok = is_ref($foo, "HASH");

	$ok = is_blessed($foo);
	$ok = is_blessed($foo, "IO::Handle");
	$c = blessed_class($foo);
	$ok = is_strictly_blessed($foo, "IO::Pipe::End");
	$ok = is_able($foo, ["print", "flush"]);

=head1 DESCRIPTION

This module provides various type-testing functions.  These are intended
for functions that, unlike most Perl code, care what type of data they
are operating on.  For example, some functions wish to behave differently
depending on the type of their arguments (like overloaded functions
in C++).

These functions only provide type classification; they do not enforce
type restrictions.  Type enforcement may, of course, be built using
these classification functions, but the reader's attention is drawn
to L<Params::Validate>.

=cut

package Params::Classify;

use warnings;
use strict;

use Exporter;
use Scalar::Util 1.10 qw(blessed reftype);

our $VERSION = "0.001";

our @ISA = qw(Exporter);

our @EXPORT_OK = qw(
	scalar_class
	is_undef
	is_string is_number
	is_glob
	is_ref ref_type
	is_blessed blessed_class is_strictly_blessed is_able
);

=head1 TYPE CLASSIFICATION

This module divides up scalar values into the following classes:

=over

=item *

undef

=item *

string (defined ordinary scalar)

=item *

typeglob

=item *

reference to unblessed object (further classified by physical data type
of the referenced object)

=item *

reference to blessed object (further classified by class blessed into)

=back

These classes are mutually exclusive and should be exhaustive.  This
classification has been chosen as the most useful when one wishes to
discriminate between types of scalar.  Other classifications are possible.
(For example, the two reference classes are distinguished by a feature of
the referenced object; Perl does not internally treat this as a feature
of the reference.)

=head1 FUNCTIONS

Each of these functions takes one scalar argument to be tested, possibly
with other arguments specifying details of the test.  Any scalar value is
acceptable for the argument to be tested (called ARG below).  Each "is_"
function returns a simple boolean result.

=head2 Classification

=over

=item scalar_class(ARG)

Determines which of the five classes described above ARG falls into.
Returns "UNDEF", "STRING", "GLOB", "REF", or "BLESSED" accordingly.

=cut

sub scalar_class($) {
	my($arg) = @_;
	my $type = reftype(\$arg);
	if($type eq "SCALAR") {
		$type = defined($arg) ? "STRING" : "UNDEF";
	} elsif($type eq "REF") {
		$type = "BLESSED" if defined(blessed($arg));
	}
	$type;
}

=back

=head2 The Undefined Value

=over

=item is_undef(ARG)

Returns true iff ARG is C<undef>.  This is precisely equivalent to
C<!defined(ARG)>, and is included for completeness.

=cut

sub is_undef($) {
	my($arg) = @_;
	!defined($arg);
}

=back

=head2 Strings

=over

=item is_string(ARG)

This returns true iff ARG is defined and is an ordinary scalar value
(not a reference or a typeglob).  This is what one usually thinks of as a
string in Perl.  In fact, any scalar (including C<undef> and references)
can be coerced to a string, but if you're trying to classify a scalar
then you don't want to do that.

=cut

sub is_string($) {
	my($arg) = @_;
	defined($arg) && reftype(\$arg) eq "SCALAR";
}

=item is_number(ARG)

This returns true iff ARG is defined and an ordinary scalar (i.e.,
satisfies C<is_string> above) and is an acceptable number to Perl.
This is what one usually thinks of as a number.

Note that simple (C<is_string>-satisfying) scalars may have independent
numeric and string values, despite the usual pretence that they have
only one value.  Such a scalar is deemed to be a number if I<either> it
already has a numeric value (e.g., was generated by a numeric literal or
an arithmetic computation) I<or> its string value has acceptable syntax
for a number (so it can be converted).  Where a scalar has separate
numeric and string values (see L<Scalar::Util/dualvar>), it is possible
for it to have an acceptable numeric value while its string value does
I<not> have acceptable numeric syntax.  Be careful to use such a value
only in a numeric context, if you are using it as a number.  C<0+ARG>
is sufficient to collapse it to an ordinary number if you want the
numeric value in string form.

A number may be either a native integer or a native floating point value.
There are several subtypes of floating point value.  For classification
among floating point values see L<Data::Float>.

This function differs from C<looks_like_number> (see
L<Scalar::Util/looks_like_number>; also L<perlapi/looks_like_number>
for a lower-level description) in excluding C<undef>, typeglobs,
and references.  Why C<looks_like_number> returns true for C<undef>
or typeglobs is anybody's guess.  References, if treated as numbers,
evaluate to the address in memory that they reference; this is useful
for comparing references for equality, but it is not otherwise useful
to treat references as numbers.  Blessed references may have overloaded
numeric operators, but if so then they don't necessarily behave like
ordinary numbers.  C<looks_like_number> is also confused by dualvars:
it looks at the string portion of the scalar.

=cut

sub is_number($) {
	my($arg) = @_;
	return 0 unless defined($arg) && reftype(\$arg) eq "SCALAR";
	my $warned;
	local $SIG{__WARN__} = sub { $warned = 1; };
	{ no warnings "void"; 0 + $arg; }
	return !$warned;
}

=back

=head2 Typeglobs

=over

=item is_glob(ARG)

Returns true iff ARG is a typeglob.  Yes, typeglobs fit into scalar
variables.

=cut

sub is_glob($) {
	my($arg) = @_;
	reftype(\$arg) eq "GLOB";
}

=back

=head2 References to Unblessed Objects

=over

=item is_ref(ARG)

Returns true iff ARG is a reference to an unblessed object.  If it
is, then the referenced data type can be determined using C<ref_type>
(see below), which will return a string such as "HASH" or "SCALAR".

=item ref_type(ARG)

Returns C<undef> if ARG is not a reference to an unblessed object.
Otherwise, determines what type of object is referenced.  Returns
"SCALAR", "ARRAY", "HASH", "CODE", "FORMAT", or "IO" accordingly.

Note that, unlike C<ref>, this does not distinguish between different
types of referenced scalar.  A reference to a string and a reference to
a reference will both return "SCALAR".  Consequently, what C<ref_type>
returns for a particular reference will not change due to changes in
the value of the referent, except for the referent being blessed.

=item is_ref(ARG, TYPE)

TYPE must be a string.  Returns true iff ARG is a reference to an
unblessed object of type TYPE, as determined by C<ref_type> (above).
Possible TYPEs are "SCALAR", "ARRAY", "HASH", "CODE", "FORMAT", and "IO".

=cut

{
	my %xlate_reftype = (
		REF    => "SCALAR",
		SCALAR => "SCALAR",
		LVALUE => "SCALAR",
		GLOB   => "SCALAR",
		ARRAY  => "ARRAY",
		HASH   => "HASH",
		CODE   => "CODE",
		FORMAT => "FORMAT",
		IO     => "IO",
	);

	sub ref_type($) {
		my($arg) = @_;
		my $reftype = reftype($arg);
		return undef unless
			defined($reftype) && !defined(blessed($arg));
		my $xlated_reftype = $xlate_reftype{$reftype};
		die "unknown reftype `$reftype', please update me"
			unless defined $xlated_reftype;
		$xlated_reftype;
	}

	sub is_ref($;$) {
		my($arg, $type) = @_;
		my $reftype = reftype($arg);
		return undef unless
			defined($reftype) && !defined(blessed($arg));
		return 1 if !defined($type);
		my $xlated_reftype = $xlate_reftype{$reftype};
		die "unknown reftype `$reftype', please update me"
			unless defined $xlated_reftype;
		$xlated_reftype eq $type;
	}
}

=back

=head2 References to Blessed Objects

=over

=item is_blessed(ARG)

Returns true iff ARG is a reference to a blessed object.  If it is,
then the class into which the object was blessed can be determined using
C<blessed_class> (see below).

=item is_blessed(ARG, CLASS)

CLASS must be a string.  Returns true iff ARG is a reference to a blessed
object that claims to be an instance of CLASS (via its C<isa> method;
see L<perlobj/isa>).

=cut

sub is_blessed($;$) {
	my($arg, $class) = @_;
	defined(blessed($arg)) && (!defined($class) || $arg->isa($class));
}

=item blessed_class(ARG)

Returns C<undef> if ARG is not a reference to a blessed object.
Otherwise, returns the class into which the object is blessed.

C<ref> (see L<perlfunc/ref>) gives the same result on references
to blessed objects, but different results on other types of value.
C<blessed_class> is actually identical to C<Scalar::Util::blessed>
(see L<Scalar::Util/blessed>).

=cut

*blessed_class = \&blessed;

=item is_strictly_blessed(ARG)

Returns true iff ARG is a reference to a blessed object, identically
to C<is_blessed>.  This exists only for symmetry; the useful form of
C<is_strictly_blessed> appears below.

=item is_strictly_blessed(ARG, CLASS)

CLASS must be a string.  Returns true iff ARG is a reference to an object
blessed into class CLASS exactly.  Because this excludes subclasses,
this is rarely what one wants, but there are some specialised occasions
where it is useful.

=cut

sub is_strictly_blessed($;$) {
	my($arg, $class) = @_;
	my $blessed = blessed($arg);
	defined($blessed) && (!defined($class) || $blessed eq $class);
}

=item is_able(ARG, METHODS)

METHODS must be either a single method name or a reference to an array
of method names.  Each method name is a string.  Returns true iff ARG is
a reference to a blessed object that claims to implement the specified
methods (via its C<can> method; see L<perlobj/can>).  This interface
check is often more appropriate than a direct ancestry check (such as
C<is_blessed> performs).

=cut

sub is_able($$) {
	my($arg, $methods) = @_;
	return 0 unless defined blessed $arg;
	foreach my $method (ref($methods) eq "" ? $methods : @$methods) {
		return 0 unless $arg->can($method);
	}
	1;
}

=back

=head1 BUGS

Probably ought to handle C<Params::Validate>'s scalar type specification
system, which makes much the same distinctions.

=head1 SEE ALSO

L<Data::Float>,
L<Params::Validate>,
L<Scalar::Util>

=head1 AUTHOR

Andrew Main (Zefram) <zefram@fysh.org>

=head1 COPYRIGHT

Copyright (C) 2004, 2006 Andrew Main (Zefram) <zefram@fysh.org>

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

=cut

1;
