In Rebol2, R3-Alpha, and Red...doing a FIND searching for a TYPESET! will give you the first instance of that type in a block:
>> find [1 2 "abc" 3 4] any-string!
== ["abc" 3 4]
>> find [1 2 <abc> 3 4] any-string!
== [<abc> 3 4]
One would think that you'd be able to search for the typeset literally by using /ONLY. But that doesn't work (though Red says they addressed this recently)
Why Wasn't This Taken Further?
Trying to FIND a function is pretty rare. So why didn't they make it so that passing a function to FIND makes it search?
>> find [1 2 3 4] func [x] [x > 2]
== [3 4]
If a function took multiple arguments, that could be asking it to effectively /SKIP and group items at a time:
>> find [1 2 4 3 5 6] func [a b] [a > b]
== [4 3 5 6]
/ONLY could have worked for finding a function literally:
>> find/only reduce [:positive? :zero? :negative?] :zero?
== [#[native! zero?...] #[native! negative?...]]
2 Likes
I Remember First Seeing The R3-Alpha Code For Find...
It was a semantic nightmare, and I remember thinking "is the code really just full of issues like this?" (yes, it was, and Ren-C has been stamping them out)
Here you see it's a complete crapshoot as to what you're going to get back. Maybe it'll point at a datatype. Maybe a typeset. Maybe an instance...it didn't discriminate.
// Find a datatype in block:
else if (IS_DATATYPE(target) || IS_TYPESET(target)) {
for (; index >= start && index < end; index += skip) {
value = BLK_SKIP(series, index);
// Used if's so we can trace it...
if (IS_DATATYPE(target)) {
if ((REBINT)VAL_TYPE(value) == VAL_DATATYPE(target)) return index;
if (IS_DATATYPE(value) && VAL_DATATYPE(value) == VAL_DATATYPE(target)) return index;
}
if (IS_TYPESET(target)) {
if (TYPE_CHECK(target, VAL_TYPE(value))) return index;
if (IS_DATATYPE(value) && TYPE_CHECK(target, VAL_DATATYPE(value))) return index;
if (IS_TYPESET(value) && EQUAL_TYPESET(value, target)) return index;
}
if (flags & AM_FIND_MATCH) break;
}
return NOT_FOUND;
}
I suppose the thought process behind this would be that if you were looking in a block for a datatype or typeset, it was probably a block that didn't contain other things. So it assumes you wouldn't be mixing datatypes and integers in the same block.
But if you're using a block as a kind of mapping structure, you may well be mixing keys that are sometimes datatypes and sometimes instances. UPARSE does this... it doesn't have separate mapping tables for finding the combinator registered for the general datatype of WORD! vs. a specific WORD! that acts as a keyword. It just has one big table (implemented with map, but you could imagine it being done differently if it were working in a row-oriented manner).
1 Like
ACTION!/DATATYPE! Antiforms To The Rescue!

As with most other places that needed /ONLY
historically, antiforms solve this... since you can't put ACTION! or DATATYPE! into lists, there's no issue.
Also, you can create matching functions from arbitrary type blocks:
>> matches [integer! tag!]
== \~&[frame! [value]]~\ ; antiform
>> find [a b c 1 2 3] matches [integer! tag!]
== [1 2 3]
Remember that antiforms cannot be put into blocks. Only quasiforms (which are real values, that evaluate to antiforms). So there's no gray area here--it means what it means--match instances of the type.
1 Like