I had what I thought was a nice clean implementation that factored out the
coercion I wanted into a single place (to avoid having to re-write it in many
places), but it doesn't seem to be allowed...
I'm trying to set up a type that matches a restricted class of string, with two
variants that can each coerce from the other.
I'm working with various bioinformatics packages, and some take a chromosome
designation as 'chr1', 'chr2', ... 'chrX', etc. while others use the same
values but omit the leading 'chr' and just use '1', '2', ..., 'X', etc. The
values that can follow the optional leading 'chr' depends upon the species (in
particular, the highest number for human is 'chr22', but it is different for
other species; and in special cases there can be more than 'chrX' and 'chrY'
('chrM' gets used for human). Since I'm working with both classes of library,
I can get input files in either format, have code that wishes to process (and
would prefer to use a single format for that code), and then write output files
in either format, so I wanted types that would accept either format and convert
to the other. But then I want to take that result and apply additional
sub-type matching on it.
I wanted to define this as:
subtype 'Type::NoChr',
as 'Str',
where { $_ !~ /^chr/ };
subtype 'Type::WithChr',
as 'Str',
where { $_ =~ /^chr/ };
coerce 'Type::WithChr',
from 'Type::NoChr',
via { 'chr' . $_ };
coerce 'Type::NoChr',
from 'Type::WithChr',
via { substr( $_, 3 ) };
That provided the ability to accept a string that expected (or not) a leading
'chr' and adding (stripping) it if it was not there. But I wanted to follow
that with further requirements on the rest of the string. So for my human
chromosome class I used:
sub _human_chr {
return 0 unless my ( $l, $v ) = $_[0] =~ /^(?:chr)?(?:([XYM])|(\d+))$/;
return 1 if $l || ( 1 <= $v && $v <= 22 );
return 0;
}
subtype 'Type::Chr::Human',
as 'Type::WithChr',
where { _human_chr($_) },
message { " - must be 'chr1'..'chr22', 'chrX', 'chrY', or 'chrM'. (The
leading 'chr' is optional.) Found: $_" };
subtype 'Chr::Human::NoChr',
as 'Type::NoChr',
where { _human_chr($_) },
message {
" - must be '1'..'22', 'X', 'Y', or 'M'. (An optional leading 'chr'
will be removed.) Found: $_"
};
However, when I go to use this subtype:
has 'chr' => ( is => 'ro',
isa => 'Type::Chr::Human',
required => 1,
# coerce => 1,
);
without the coerce, it does not do the conversion and only allows input that
has the explicit leading 'chr', but with the coerce parameter it gives a
compile time error complaining that the type needs to have a coercion.
Am I going to have to write the duplicate coercion code for every one of the
'real' sub-types that want to also have this optional prefix behaviour, or is
there an alternate way to write this?
John Macdonald
Software Engineer
Ontario Institute for Cancer Research
MaRS Centre
661 University Avenue
Suite 510
Toronto, Ontario
Canada M5G 0A3
Tel:
Email: [email protected]
Toll-free: 1-866-678-6427
Twitter: @OICR_news
www.oicr.on.ca<http://www.oicr.on.ca/>
This message and any attachments may contain confidential and/or privileged
information for the sole use of the intended recipient. Any review or
distribution by anyone other than the person for whom it was originally
intended is strictly prohibited. If you have received this message in error,
please contact the sender and delete all copies. Opinions, conclusions or other
information contained in this message may not be that of the organization.