The original COMBINATOR implicitly gave you an argument called INPUT:
tally: combinator [
"Iterate a rule and count the number of times it matches"
return: "Number of matches (can be 0)"
[integer!]
parser [action!]
][
let count: 0
cycle [
[^ input]: parser input except [ ; INPUT not mentioned on interface
return count ; updated input will be grafted into pack! w/count
]
count: count + 1
]
]
You only have to specify the number of parsers (or non-combinated literal arguments) you take.
I observed an inconsistency, that the <input> combinator returns the input to the overall parse operation at its original position... not the current position.
Then I thought "hm, I don't know whether to change the argument to be called POSITION or POS.
But Why Was The Argument Implicit To Begin With?
Making it explicit is clearer, and lets you pick your own name for it:
tally: combinator [
"Iterate a rule and count the number of times it matches"
return: "Number of matches (can be 0)"
[integer!]
pos [any-series?] ; Maybe I like POS better than POSITION for arg 1
parser [action!]
][
let count: 0
cycle [
[^ pos]: parser pos except [
return count
]
count: count + 1
]
]
This also means that if your combinator is only applicable to specific series types (e.g. just lists, or just strings) then you can say that, and have type checking take care of it vs. having to put the check in the body of your combinator.