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: john.macdon...@oicr.on.ca

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.

Reply via email to