@@ -214,7 +214,17 @@ part of the L<colon-pair|/type/Pair>.
214
214
type constraint,:D;type constraint,:U;type constraint,:_>
215
215
216
216
Normally, a type constraint only checks whether the value of the parameter is of the
217
- correct type.
217
+ correct type. Crucially, both I < object instances > and I < type objects > will satisfy
218
+ such a constraint as illustrated below:
219
+
220
+ say 42.WHAT # OUTPUT: «(Int)»
221
+ say 42 ~~ Int # OUTPUT: «True»
222
+ say Int ~~ Int # OUTPUT: «True»
223
+
224
+ Note how both C < 42 > and C < Int > satisfy the match.
225
+
226
+ Sometimes we need to distinguish between these object instances (C < 42 > )
227
+ and type objects (C < Int > ). Consider the following code:
218
228
219
229
sub limit-lines(Str $s, Int $limit) {
220
230
my @lines = $s.lines;
@@ -229,10 +239,20 @@ correct type.
229
239
# (Str:D $: *%_)»
230
240
say limit-lines "a \n b", Int # Always returns the max number of lines
231
241
232
- In the code above, we really only want to deal with string instances, not
233
- type objects. To do this, we use the C < :D > type constraint. This constraint
234
- checks the value passed is an I < object instance > , in a similar fashion to calling
235
- the C < .DEFINITE > method on the value:
242
+ Here we really only want to deal with string instances, not
243
+ type objects. To do this, we can use the C < :D > type constraint. This constraint
244
+ checks that the value passed is an I < object instance > , in a similar fashion to calling
245
+ its L < DEFINITE|/language/mop#DEFINITE > method.
246
+
247
+ To warm up, let's apply C < :D > to the right hand side of our humble C < Int > example:
248
+
249
+ say 42 ~~ Int:D # OUTPUT: «True»
250
+ say Int ~~ Int:D # OUTPUT: «False»
251
+
252
+ Note how only C < 42 > matches C < Int:D > in the above.
253
+
254
+ Returning to C < limit-lines > , we can now amend its signature to catch the error
255
+ early:
236
256
237
257
sub limit-lines(Str:D $s, Int $limit) { };
238
258
say limit-lines Str, 3;
@@ -243,10 +263,18 @@ the C<.DEFINITE> method on the value:
243
263
This is much better than the way the program failed before, since here the
244
264
reason for failure is clearer.
245
265
246
- It's also possible that type objects are the only ones that make
266
+ It's also possible that I < type objects > are the only ones that make
247
267
sense for a routine to accept. This can be done with the C < :U > type
248
- constraint, which checks the value passed if it is a I < type object > ,
249
- rather than an object instance.
268
+ constraint, which checks whether the value passed is a type object
269
+ rather than an object instance. Here's our C < Int > example again, this
270
+ time with C < :U > applied:
271
+
272
+ say 42 ~~ Int:U # OUTPUT: «False»
273
+ say Int ~~ Int:U # OUTPUT: «True»
274
+
275
+ Now C < 42 > fails to match C < Int:U > while C < Int > succeeds.
276
+
277
+ Here's a more practical example:
250
278
251
279
sub can-turn-into(Str $string, Any:U $type) {
252
280
return so $string.$type;
@@ -257,6 +285,12 @@ rather than an object instance.
257
285
say can-turn-into("a string", Num);
258
286
# OUTPUT: True True True False
259
287
288
+ Calling C < can-turn-into > with an object instance as its second parameter
289
+ will yield a constraint violation as intended:
290
+
291
+ say can-turn-into("a string", 123);
292
+ # OUTPUT: «Parameter '$type' of routine 'can-turn-into' must be a type object of type 'Any', not an object instance of type 'Int'...»
293
+
260
294
For explicitly indicating the normal behaviour, C < :_ > can be used, but this is
261
295
unnecessary. C < :(Num:_ $) > is the same as C < :(Num $) > .
262
296
0 commit comments