develooper Front page | perl.moose | Postings from June 2014

coerce a sub-subtype

Thread Next
John Macdonald
June 16, 2014 18:41
coerce a sub-subtype
Message ID:
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



Toll-free: 1-866-678-6427
Twitter: @OICR_news<>

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.

Thread Next Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at | Group listing | About