type constraint :D

Documentation for type constraint :D, assembled from the following types:

class Signature

From Signature

(Signature) type constraint :D

Normally, a type constraint only checks whether the value of the parameter is of the correct type.

sub limit-lines(Str $sInt $limit{
    my @lines = $s.lines;
    @lines[0 .. min @lines.elems$limit].join("\n")
say (limit-lines "\n b \n c \n d \n"3).perl# "a \n b \n c \n d " 
say limit-lines Str3;
CATCH { default { put .^name''.Str } };
# OUTPUT: «X::Multi::NoMatch: Cannot resolve caller lines(Str: ); none of these signatures match: 
#     (Str:D $: :$count!, *%_) 
#     (Str:D $: $limit, *%_) 
#     (Str:D $: *%_)» 
say limit-lines "\n b"Int # Always returns the max number of lines 

In the code above, we really only want to deal with string instances, not type objects. To do this, we use the :D type constraint. This constraint checks the value passed is an object instance, in a similar fashion to calling the .DEFINITE method on the value:

sub limit-lines(Str:D $sInt $limit{ };
say limit-lines Str3;
CATCH { default { put .^name ~ '--' ~ .Str } };
# OUTPUT: «Parameter '$s' of routine 'limit-lines' must be an object instance of type 'Str', 
#          not a type object of type 'Str'.  Did you forget a '.new'?» 

This is much better than the way the program failed before, since here the reason for failure is clearer.

It's also possible that type objects are the only ones that make sense for a routine to accept. This can be done with the :U type constraint, which checks the value passed if it is a type object, rather than an object instance.

sub can-turn-into(Str $þingAny:U $type{
   return so $þing.$type;
say can-turn-into("3"Int);
say can-turn-into("6.5"Int);
say can-turn-into("6.5"Num);
say can-turn-into("a string"Num);
# OUTPUT: True True True False 

For explicitly indicating the normal behaviour, :_ can be used, but this is unnecessary. :(Num:_ $) is the same as :(Num $).

To recap, here is a quick illustration of these type constraints, also known collectively as type smileys:

# Checking a type object 
say Int ~~ Any:D;    # OUTPUT: «False␤» 
say Int ~~ Any:U;    # OUTPUT: «True␤» 
say Int ~~ Any:_;    # OUTPUT: «True␤» 
# Checking an object instance 
say 42 ~~ Any:D;     # OUTPUT: «True␤» 
say 42 ~~ Any:U;     # OUTPUT: «False␤» 
say 42 ~~ Any:_;     # OUTPUT: «True␤» 
# Checking a user-supplied class 
class Foo {};
say Foo ~~ Any:D;    # OUTPUT: «False␤» 
say Foo ~~ Any:U;    # OUTPUT: «True␤» 
say Foo ~~ Any:_;    # OUTPUT: «True␤» 
my $f = Foo.new;
say $f  ~~ Any:D;    # OUTPUT: «True␤» 
say $f  ~~ Any:U;    # OUTPUT: «False␤» 
say $f  ~~ Any:_;    # OUTPUT: «True␤» 

The Classes and Objects document further elaborates on the concepts of instances and type objects and discovering them with the .DEFINITE method.

Keep in mind all parameters have values; even optional ones have default defaults that are the type object of the constrained type for explicit type constraints. If no explicit type constraint exists, the default default is an Any type object for methods, submethods, and subroutines, and a Mu type object for blocks. This means that if you use the :D type smiley, you'd need to provide a default value or make the parameter required. Otherwise, the default default would be a type object, which would fail the definiteness constraint.

sub divide (Int:D :$a = 2Int:D :$b!{ say $a/$b }
divide :1a, :2b; # OUTPUT: «0.5␤»