summaryrefslogtreecommitdiff
path: root/lib/Type/Tiny/Manual/UsingWithMoose.pod
blob: e649ba0f0d26064ec70d0d8f1f41a3734f0f3a25 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
=pod

=encoding utf-8

=head1 NAME

Type::Tiny::Manual::UsingWithMoose - how to use Type::Tiny with Moose

=head1 MANUAL

First read L<Type::Tiny::Manual::Moo>, L<Type::Tiny::Manual::Moo2>, and
L<Type::Tiny::Manual::Moo3>. Everything in those parts of the manual
should work exactly the same in Moose.

This part of the manual will focus on Moose-specifics.

=head2 Why Use Type::Tiny At All?

Moose does have a built-in type constraint system which is fairly
convenient to use, but there are several reasons you should consider
using Type::Tiny instead.

=over

=item *

Type::Tiny type constraints will usually be faster than Moose built-ins.
Even without Type::Tiny::XS installed, Type::Tiny usually produces more
efficient inline code than Moose. Coercions will usually be a lot faster.

=item *

Type::Tiny provides helpful methods like C<where> and C<plus_coercions>
that allow type constraints and coercions to be easily tweaked on a
per-attribute basis.

Something like this is much harder to do with plain Moose types:

  has name => (
    is      => "ro",
    isa     => Str->plus_coercions(
      ArrayRef[Str], sub { join " ", @$_ },
    ),
    coerce  => 1,
  );

Moose tends to encourage defining coercions globally, so if you wanted
one B<Str> attribute to be able to coerce from B<< ArrayRef[Str] >>, then
I<all> B<Str> attributes would coerce from B<< ArrayRef[Str] >>, and they'd
all do that coercion in the same way. (Even if it might make sense to
join by a space in some places, a comma in others, and a line break in
others!)

=item *

Type::Tiny provides automatic deep coercions, so if type B<Xyz> has a coercion,
the following should "just work":

  has xyzlist => ( is => 'ro', isa => ArrayRef[Xyz], coerce => 1 );

=item *

Type::Tiny offers a wider selection of built-in types.

=item *

By using Type::Tiny, you can use the same type constraints and coercions
for attributes and method parameters, in Moose and non-Moose code.

=back

=head2 Type::Utils

If you've used L<Moose::Util::TypeConstraints>, you may be accustomed to
using a DSL for declaring type constraints:

  use Moose::Util::TypeConstraints;
  
  subtype 'Natural',
    as 'Int',
    where { $_ > 0 };

There's a module called L<Type::Utils> that provides a very similar DSL for
declaring types in Type::Library-based type libraries.

  package My::Types {
    use Type::Library -base;
    use Type::Utils;
    use Types::Standard qw( Int );
    
    declare 'Natural',
      as Int,
      where { $_ > 0 };
  }

Personally I prefer the more object-oriented way to declare types though.

Since Type::Library 1.012, a shortcut has been available for importing
Type::Library and Type::Utils at the same time:

  package MyType {
    use Type::Library -base, -utils;
    
    ...;
  }

In Moose you might also declare types like this within classes and roles too.
Unlike Moose, Type::Tiny doesn't keep types in a single global flat namespace,
so this doesn't work quite the same with Type::Utils. It still creates the
type, but it doesn't store it in any type library; the type is returned.

  package My::Class {
    use Moose;
    use Type::Utils;
    use Types::Standard qw( Int );
    
    my $Natural =          # store type in a variable
      declare 'Natural',
      as Int,
      where { $_ > 0 };
    
    has number => ( is => 'ro', isa => $Natural );
  }

But really, isn't the object-oriented way cleaner?

  package My::Class {
    use Moose;
    use Types::Standard qw( Int );
    
    has number => (
      is   => 'ro',
      isa  => Int->where('$_ > 0'),
    );
  }

=head2 Type::Tiny and MooseX::Types

L<Types::Standard> should be a drop-in replacement for L<MooseX::Types>.
And L<Types::Common::Numeric> and L<Types::Common::String> should easily
replace L<MooseX::Types::Common::Numeric> and L<MooseX::Types::Common::String>.

That said, if you do with to use a mixture of Type::Tiny and MooseX::Types,
they should fit together pretty seamlessly.

  use Types::Standard qw( ArrayRef );
  use MooseX::Types::Common::Numeric qw( PositiveInt );
  
  # this should just work
  my $list_of_nums = ArrayRef[PositiveInt];
  
  # and this
  my $list_or_num = ArrayRef | PositiveInt;

=head2 C<< -moose >> Import Parameter

If you have read this far in the manual, you will know that this is the
usual way to import type constraints:

  use Types::Standard qw( Int );

And the C<Int> which is imported is a function that takes no arguments and
returns the B<Int> type constraint, which is a blessed object in the
L<Type::Tiny> class.

Type::Tiny mocks the L<Moose::Meta::TypeConstraint> API so well that most
Moose and MooseX code will not be able to tell the difference.

But what if you need a real Moose::Meta::TypeConstraint object?

  use Types::Standard -moose, qw( Int );

Now the C<Int> function imported will return a genuine native Moose type
constraint.

This flag is mostly a throwback from when Type::Tiny native objects
I<< didn't >> directly work in Moose. In 99.9% of cases, there is no
reason to use it and plenty of reasons not to. (Moose native type
constraints don't offer helpful methods like C<plus_coercions> and
C<where>.)

=head2 C<< moose_type >> Method

Another quick way to get a native Moose type constraint object from a
Type::Tiny object is to call the C<moose_type> method:

  use Types::Standard qw( Int );
  
  my $tiny_type   = Int;
  my $moose_type  = $tiny_type->moose_type;

Internally, this is what the C<< -moose >> flag makes imported functions
do.

=head1 NEXT STEPS

Here's your next step:

=over

=item * L<Type::Tiny::Manual::UsingWithMouse>

How to use Type::Tiny with Mouse, including the advantages of Type::Tiny
over built-in type constraints, and Mouse-specific features.

=back

=head1 AUTHOR

Toby Inkster E<lt>tobyink@cpan.orgE<gt>.

=head1 COPYRIGHT AND LICENCE

This software is copyright (c) 2013-2014, 2017-2023 by Toby Inkster.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=head1 DISCLAIMER OF WARRANTIES

THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.

=cut