Skip to content

Commit 9c70cde

Browse files
committed
Add alpha implementation of structured types
The syntax for defininig these types and the guts may change in the future, but this is a good enough start to release for others to look at.
1 parent 57f74a9 commit 9c70cde

File tree

10 files changed

+1733
-0
lines changed

10 files changed

+1733
-0
lines changed

.stopwords

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,7 @@ parameterization
4747
parameterized
4848
reimplementation
4949
sigils
50+
slurpy
51+
structurable
5052
subtype
5153
subtypes

lib/Specio/Constraint/Structurable.pm

Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
package Specio::Constraint::Structurable;
2+
3+
use strict;
4+
use warnings;
5+
6+
our $VERSION = '0.34';
7+
8+
use Carp qw( confess );
9+
use Role::Tiny::With;
10+
use Scalar::Util qw( blessed );
11+
use Specio::DeclaredAt;
12+
use Specio::OO;
13+
use Specio::Constraint::Structured;
14+
use Specio::TypeChecks qw( does_role isa_class );
15+
16+
use Specio::Constraint::Role::Interface;
17+
with 'Specio::Constraint::Role::Interface';
18+
19+
{
20+
## no critic (Subroutines::ProtectPrivateSubs)
21+
my $role_attrs = Specio::Constraint::Role::Interface::_attrs();
22+
## use critic
23+
24+
my $attrs = {
25+
%{$role_attrs},
26+
_parameterization_args_builder => {
27+
isa => 'CodeRef',
28+
init_arg => 'parameterization_args_builder',
29+
required => 1,
30+
},
31+
_name_builder => {
32+
isa => 'CodeRef',
33+
init_arg => 'name_builder',
34+
required => 1,
35+
},
36+
_structured_constraint_generator => {
37+
isa => 'CodeRef',
38+
init_arg => 'structured_constraint_generator',
39+
predicate => '_has_structured_constraint_generator',
40+
},
41+
_structured_inline_generator => {
42+
isa => 'CodeRef',
43+
init_arg => 'structured_inline_generator',
44+
predicate => '_has_structured_inline_generator',
45+
},
46+
};
47+
48+
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
49+
sub _attrs {
50+
return $attrs;
51+
}
52+
}
53+
54+
sub BUILD {
55+
my $self = shift;
56+
57+
if ( $self->_has_constraint ) {
58+
die
59+
'A structurable constraint with a constraint parameter must also have a structured_constraint_generator'
60+
unless $self->_has_structured_constraint_generator;
61+
}
62+
63+
if ( $self->_has_inline_generator ) {
64+
die
65+
'A structurable constraint with an inline_generator parameter must also have a structured_inline_generator'
66+
unless $self->_has_structured_inline_generator;
67+
}
68+
69+
return;
70+
}
71+
72+
sub parameterize {
73+
my $self = shift;
74+
my %args = @_;
75+
76+
my $declared_at = $args{declared_at};
77+
78+
if ($declared_at) {
79+
isa_class( $declared_at, 'Specio::DeclaredAt' )
80+
or confess
81+
q{The "declared_at" parameter passed to ->parameterize must be a Specio::DeclaredAt object};
82+
}
83+
84+
my %parameters
85+
= $self->_parameterization_args_builder->( $self, $args{of} );
86+
87+
$declared_at = Specio::DeclaredAt->new_from_caller(1)
88+
unless defined $declared_at;
89+
90+
my %new_p = (
91+
parent => $self,
92+
parameters => \%parameters,
93+
declared_at => $declared_at,
94+
name => $self->_name_builder->( $self, \%parameters ),
95+
);
96+
97+
if ( $self->_has_structured_constraint_generator ) {
98+
$new_p{constraint}
99+
= $self->_structured_constraint_generator->(%parameters);
100+
}
101+
else {
102+
for my $p (
103+
grep {
104+
blessed($_)
105+
&& does_role('Specio::Constraint::Role::Interface')
106+
} values %parameters
107+
) {
108+
109+
confess
110+
q{Any type objects passed to ->parameterize must be inlinable constraints if the structurable type has an inline_generator}
111+
unless $p->can_be_inlined;
112+
}
113+
114+
my $ig = $self->_structured_inline_generator;
115+
$new_p{inline_generator}
116+
= sub { $ig->( shift, shift, %parameters, @_ ) };
117+
}
118+
119+
return Specio::Constraint::Structured->new(%new_p);
120+
}
121+
122+
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
123+
sub _name_or_anon {
124+
return $_[1]->_has_name ? $_[1]->name : 'ANON';
125+
}
126+
## use critic
127+
128+
__PACKAGE__->_ooify;
129+
130+
1;
131+
132+
# ABSTRACT: A class which represents structurable constraints
133+
134+
__END__
135+
136+
=pod
137+
138+
=for Pod::Coverage BUILD
139+
140+
=head1 SYNOPSIS
141+
142+
my $tuple = t('Tuple');
143+
144+
my $tuple_of_str_int = $tuple->parameterize( of => [ t('Str'), t('Int') ] );
145+
146+
=head1 DESCRIPTION
147+
148+
This class implements the API for structurable types like C<Dict>, C<Map>< and
149+
C<Tuple>.
150+
151+
=head1 API
152+
153+
This class implements the same API as L<Specio::Constraint::Simple>, with a few
154+
additions.
155+
156+
=head2 Specio::Constraint::Structurable->new(...)
157+
158+
This class's constructor accepts two additional parameters:
159+
160+
=over 4
161+
162+
=item * parameterization_args_builder
163+
164+
This is a subroutine that takes the values passed to C<of> and returns a hash
165+
of named arguments. These arguments will then be passed into the
166+
C<structured_constraint_generator> or C<structured_inline_generator>.
167+
168+
This should also do argument checking to make sure that the argument passed
169+
are valid. For example, the C<Tuple> type turns the arrayref passed to C<of>
170+
into a hash, along the way checking that the caller did not do things like
171+
interleave optional and required elements or mix optional and slurpy together
172+
in the definition.
173+
174+
This parameter is required.
175+
176+
=item * name_builder
177+
178+
This is a subroutine that is called to generate a name for the structured type
179+
when it is created. This will be called as a method on the
180+
C<Specio::Constraint::Structurable> object. It will be passed the hash of
181+
arguments returned by the C<parameterization_args_builder>.
182+
183+
This parameter is required.
184+
185+
=item * structured_constraint_generator
186+
187+
This is a subroutine that generates a new constraint subroutine when the type
188+
is structured.
189+
190+
It will be called as a method on the type and will be passed the hash of
191+
arguments returned by the C<parameterization_args_builder>.
192+
193+
This parameter is mutually exclusive with the C<structured_inline_generator>
194+
parameter.
195+
196+
This parameter or the C<structured_inline_generator> parameter is required.
197+
198+
=item * structured_inline_generator
199+
200+
This is a subroutine that generates a new inline generator subroutine when the
201+
type is structured.
202+
203+
It will be called as a method on the L<Specio::Constraint::Structured> object
204+
when that object needs to generate an inline constraint. It will receive the
205+
type parameter as the first argument and the variable name as a string as the
206+
second.
207+
208+
The remaining arguments will be the parameter hash returned by the
209+
C<parameterization_args_builder>.
210+
211+
This probably seems fairly confusing, so looking at the examples in the
212+
L<Specio::Library::Structured::*> code may be helpful.
213+
214+
This parameter is mutually exclusive with the
215+
C<structured_constraint_generator> parameter.
216+
217+
This parameter or the C<structured_constraint_generator> parameter is
218+
required.
219+
220+
=back
221+
222+
=head2 $type->parameterize(...)
223+
224+
This method takes two arguments. The C<of> argument should be an object which
225+
does the L<Specio::Constraint::Role::Interface> role, and is required.
226+
227+
The other argument, C<declared_at>, is optional. If it is not given, then a
228+
new L<Specio::DeclaredAt> object is creating using a call stack depth of 1.
229+
230+
This method returns a new L<Specio::Constraint::Structured> object.
231+
232+
=cut

lib/Specio/Constraint/Structured.pm

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package Specio::Constraint::Structured;
2+
3+
use strict;
4+
use warnings;
5+
6+
our $VERSION = '0.34';
7+
8+
use List::Util qw( all );
9+
use Role::Tiny::With;
10+
use Specio::OO;
11+
use Specio::TypeChecks qw( does_role );
12+
use Storable qw( dclone );
13+
14+
use Specio::Constraint::Role::Interface;
15+
with 'Specio::Constraint::Role::Interface';
16+
17+
{
18+
## no critic (Subroutines::ProtectPrivateSubs)
19+
my $attrs = dclone( Specio::Constraint::Role::Interface::_attrs() );
20+
## use critic
21+
22+
$attrs->{parent}{isa} = 'Specio::Constraint::Structurable';
23+
$attrs->{parent}{required} = 1;
24+
25+
$attrs->{parameters} = {
26+
isa => 'HashRef',
27+
required => 1,
28+
};
29+
30+
## no critic (Subroutines::ProhibitUnusedPrivateSubroutines)
31+
sub _attrs {
32+
return $attrs;
33+
}
34+
}
35+
36+
sub can_be_inlined {
37+
my $self = shift;
38+
return $self->_has_inline_generator;
39+
}
40+
41+
__PACKAGE__->_ooify;
42+
43+
1;
44+
45+
# ABSTRACT: A class which represents structured constraints
46+
47+
__END__
48+
49+
=pod
50+
51+
=for Pod::Coverage can_be_inlined type_parameter
52+
53+
=head1 SYNOPSIS
54+
55+
my $tuple = t('Tuple');
56+
57+
my $tuple_of_str_int = $tuple->parameterize( of => [ t('Str'), t('Int') ] );
58+
59+
my $parent = $tuple_of_str_int->parent; # returns Tuple
60+
my $parameters = $arrayref_of_int->parameters; # returns { of => [ t('Str'), t('Int') ] }
61+
62+
=head1 DESCRIPTION
63+
64+
This class implements the API for structured types.
65+
66+
=head1 API
67+
68+
This class implements the same API as L<Specio::Constraint::Simple>, with a few
69+
additions.
70+
71+
=head2 Specio::Constraint::Structured->new(...)
72+
73+
This class's constructor accepts two additional parameters:
74+
75+
=over 4
76+
77+
=item * parent
78+
79+
This should be the L<Specio::Constraint::Structurable> object from which this
80+
object was created.
81+
82+
This parameter is required.
83+
84+
=item * parameters
85+
86+
This is the hashref of parameters for the structured type. These are the
87+
parameters returned by the C<Structurable> type's
88+
C<parameterization_args_builder>. The exact form of this hashref will vary for
89+
each structured type.
90+
91+
This parameter is required.
92+
93+
=back
94+
95+
=head2 $type->parameters
96+
97+
Returns the hashref that was passed to the constructor.
98+
99+
=cut

0 commit comments

Comments
 (0)