Search code examples
regexraku

Why the pointy block with Match in for loop output Nil?


A valid Match object is not empty or undefined in my opinion:

say Match.new(:orig("20230213112803"), :from(4), :pos(6)).elems;   # 0
say Match.new(:orig("20230213112803"), :from(4), :pos(6)).chars;   # 2
say Match.new(:orig("20230213112803"), :from(4), :pos(6)).defined; # True

but the following pointy block with a Match object in a for loop outputs Nil:

for Match.new(:orig("20230213112803"), :from(4), :pos(6)) -> $m {
    say ~$m
}
# OUTPUT: Nil

But the given statement outputs what I expected:

given Match.new(:orig("20230213112803"), :from(4), :pos(6)) -> $m {
    say ~$m
}
# OUTPUT: 02

Isn't something empty or undefined that prevents a for loop from executing?

for "a" -> $m { say $m } # a
for []  -> $m { say $m } # Nil
for {}  -> $m { say $m } # Nil

Solution

  • TL;DR given will always treat its argument as one thing (an object). In contrast, for is a looping construct, and may ask its argument to .list its elements so it can usefully loop. It does so in your example, and the Match object contains no elements, so the for doesn't call the block at all.

    given foo (or given foo, bar)

    given just makes its argument(s) the topic ("it") and calls the associated statement or block (passing in the topic). End of story.

    for foo, bar

    for foo, bar just makes each of its arguments the topic in turn (and calls the associated statement/block). Again, end of story.

    for foo

    If for only has one argument, and that argument is not explicitly marked as singular (by prefixing it with $), then for calls .list on that argument and, for each value in the returned list it sets that to "it" and calls the associated statement/block, one-by-one. That's your scenario. Again, end of story.

    Except this clearly confused you (and Liz), so there needs to be a bit more to my answer!


    Like an array or a hash, a Capture (and thus Match) object may contain zero or more elements. In all these cases the object itself is not an element.

    A Rakoon is not surprised when for [] { ... } does nothing, because the [] very obviously contains no elements, and it would be kinda silly, not to mention confusing, for the array to return itself as if it were an element of itself.

    What about for Match.new { ... }? It also does nothing, and it's perhaps obvious why not -- because it also contains no elements.

    But what about your example? Well, again, it does nothing, and again, it's because it contains no elements.

    But "what?!?" you say! Surely the match object is itself useful data? Why doesn't the for treat it like it would, say, for 42 { ... }? Well, because if you absolutely want that, then write given 42 { ... } instead. If you use for, then expect Raku to seek out a list meaning for its argument(s), not a singular meaning, and that means calling .list on its argument in the scenario in your question.

    Then what are a Match object's elements? They are its child Match objects, if any. There may be positional ones (which you can access with, for example, match-object[...], or .list), and named ones (which you can access with, for example, match-object{...} or .hash).