Lazy initialization of coercions
Fuji, Goro [Sat, 13 Nov 2010 14:13:16 +0000 (23:13 +0900)]
lib/Mouse/Meta/TypeConstraint.pm
lib/Mouse/PurePerl.pm
t/001_mouse/043-parameterized-type.t
xs-src/MouseTypeConstraints.xs

index c73fa89..15fe6e9 100644 (file)
@@ -67,9 +67,16 @@ sub new {
     $self->compile_type_constraint()
         if !$args{hand_optimized_type_constraint};
 
-    if($args{type_constraints}) {
-        $self->_compile_union_type_coercion();
+    if($args{type_constraints}) { # union types
+        foreach my $type(@{$self->{type_constraints}}){
+            if($type->has_coercion){
+                # set undef for has_coercion()
+                $self->{_compiled_type_coercion} = undef;
+                last;
+            }
+        }
     }
+
     return $self;
 }
 
@@ -120,60 +127,62 @@ sub _add_type_coercions { # ($self, @pairs)
         push @{$coercion_map}, [ $type => $action ];
     }
 
-    $self->_compile_type_coercion();
+    $self->{_compiled_type_coercion} = undef;
     return;
 }
 
-sub _compile_type_coercion {
+sub _compiled_type_coercion {
     my($self) = @_;
 
-    my @coercions;
-
-    foreach my $pair(@{$self->{coercion_map}}) {
-        push @coercions, [ $pair->[0]->_compiled_type_constraint, $pair->[1] ];
-    }
-
-    $self->{_compiled_type_coercion} = sub {
-       my($thing) = @_;
-       foreach my $pair (@coercions) {
-            #my ($constraint, $converter) = @$pair;
-            if ($pair->[0]->($thing)) {
-              local $_ = $thing;
-              return $pair->[1]->($thing);
-            }
-       }
-       return $thing;
-    };
-    return;
-}
+    my $coercion = $self->{_compiled_type_coercion};
+    return $coercion if defined $coercion;
 
-sub _compile_union_type_coercion {
-    my($self) = @_;
-
-    my @coercions;
-    foreach my $type(@{$self->{type_constraints}}){
-        if($type->has_coercion){
-            push @coercions, $type;
+    if(!$self->{type_constraints}) {
+        my @coercions;
+        foreach my $pair(@{$self->{coercion_map}}) {
+            push @coercions,
+                [ $pair->[0]->_compiled_type_constraint, $pair->[1] ];
         }
+
+        $coercion = sub {
+           my($thing) = @_;
+           foreach my $pair (@coercions) {
+                #my ($constraint, $converter) = @$pair;
+                if ($pair->[0]->($thing)) {
+                  local $_ = $thing;
+                  return $pair->[1]->($thing);
+                }
+           }
+           return $thing;
+        };
     }
-    if(@coercions){
-        $self->{_compiled_type_coercion} = sub {
-            my($thing) = @_;
-            foreach my $type(@coercions){
-                my $value = $type->coerce($thing);
-                return $value if $self->check($value);
+    else { # for union type
+        my @coercions;
+        foreach my $type(@{$self->{type_constraints}}){
+            if($type->has_coercion){
+                push @coercions, $type;
             }
-            return $thing;
-        };
+        }
+        if(@coercions){
+            $coercion = sub {
+                my($thing) = @_;
+                foreach my $type(@coercions){
+                    my $value = $type->coerce($thing);
+                    return $value if $self->check($value);
+                }
+                return $thing;
+            };
+        }
     }
-    return;
+
+    return( $self->{_compiled_type_coercion} = $coercion );
 }
 
 sub coerce {
     my $self = shift;
     return $_[0] if $self->check(@_);
 
-    my $coercion = $self->{_compiled_type_coercion}
+    my $coercion = $self->_compiled_type_coercion
         or $self->throw_error("Cannot coerce without a type coercion");
     return  $coercion->(@_);
 }
index 174f4a9..0e5ed43 100644 (file)
@@ -613,7 +613,6 @@ sub _identity  { Scalar::Util::refaddr($_[0]) } # overload 0+
 
 sub type_parameter           { $_[0]->{type_parameter} }
 sub _compiled_type_constraint{ $_[0]->{compiled_type_constraint} }
-sub _compiled_type_coercion  { $_[0]->{_compiled_type_coercion}  }
 
 sub __is_parameterized { exists $_[0]->{type_parameter} }
 sub has_coercion {       exists $_[0]->{_compiled_type_coercion} }
index 24dbac4..e3e4cda 100644 (file)
@@ -122,7 +122,8 @@ use Tie::Array;
         my $bar = Bar->new(list => [ qw(a b c) ]);
 
         is_deeply( $bar->list, \@list, "list is as expected");
-    } "coercion works";
+    } "coercion works"
+        or diag( Mouse::Util::TypeConstraints::find_type_constraint("Bar::List")->dump );
 
     throws_ok {
         Bar->new(list => [ { 1 => 2 }, 2, 3 ]);
index e1670f0..f22a869 100644 (file)
@@ -670,7 +670,6 @@ BOOT:
     INSTALL_SIMPLE_READER(TypeConstraint, type_parameter);
 
     INSTALL_SIMPLE_READER_WITH_KEY(TypeConstraint, _compiled_type_constraint, compiled_type_constraint);
-    INSTALL_SIMPLE_READER(TypeConstraint, _compiled_type_coercion); /* Mouse specific */
 
     INSTALL_SIMPLE_PREDICATE_WITH_KEY(TypeConstraint, has_coercion, _compiled_type_coercion);
     INSTALL_SIMPLE_PREDICATE_WITH_KEY(TypeConstraint, __is_parameterized, type_parameter); /* Mouse specific */