FIND treats TYPESET!s specially...why not functions?

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?...]]

Ren-C goes with QUOTED! vs. /ONLY, but same basic premise

>> find [1 0 2 0] :zero?
== [0 2 0]

>> find reduce [:positive? :zero? :negative?] quote :zero?
== [#[native! zero?...] #[native! negative?...]]

Though It Seems Easy To Make Mistakes...

People are invariably going to write find data value...think it works for a few values they try...and assume it works for others. Redbols are notorious for pulling the rug out from under you with such things.

But if you're willing to do this for typesets, I don't see why doing it for functions is that much worse.

Just something to think about.

2 Likes

I feel like we're coming to a solution here in general, with the idea of everything having isotopic forms, specifically the idea that ACTION! isotopes are the only kind of action that executes implicitly.

It would then be isotopic ACTION! and isotopic TYPESET! that would have the weird behavior.

How would you make an isotopic action to pass to something like FIND? Well, the proposal is that action isotopes are the only things that execute. Hence:

foo: func [x] [print [x]]  ; FUNC would return an ACTION! isotope
bar: x -> [print [x]]  ; LAMBDA would return an ACTION! isotope

This means that simply defining a function in the slot where isotopic things are taken would fulfill the purpose.

>> find [1 2 3 4] func [x] [x > 2]  ; pass isotopic function, so not using literally
== [3 4]  ; one interpretation of the result (could also pass function a position)

>> find [1 2 3 4] meta func [x] [x > 2]  ; search for the function *literally*
== [1 2 3 4]  ; impossible to find that function in the block, you just created it

Regarding isotopic typesets... hm. I don't know how to make those besides UNMETA on the typeset!. But the key here is that if you don't make it isotopic, you're looking for it literally.

So this gets a little bit complex to do with existing functions without GET-WORD! acting in the traditional way, because the function accessors like ^integer? give you a non-isotopic action. Hence this won't work, if you wanted it to return [2 3 4]

>> find [1 2 3 4] ^even?  ; non-isotopic ACTION! for even?
; null

You'd have to unmeta the result, and the function I have for doing that right now specific to actions is called RUNS:

>> find [1 2 3 4] runs ^even?
== [2 3 4]

That's not awful... if you think about it as an analogue to SPREAD.

Right now there's GET/ANY which will give you an isotope, but I think that looks worse:

>> find [1 2 3 4] get/any 'even?
== [2 3 4]

(Also, I'm wondering if there should be no /ANY refinement to GET, and it's merely sensitive to the use of ^META on the output...the way I'm suggesting that SELECT and such work.)

But right now, with the idea that the function generators all return isotopes, you'd still be able to use a generator in that spot:

>> find [1 2 3 4] func [x] [x > 2]  ; FUNC does make isotopes in pending code
== [3 4]

We could say that there is a term MATCHES which creates isotopic typesets from non-isotopic ones

>> find [1 2 "abc" 3 4] matches any-string!
== ["abc" 3 4]

Yet by the rules I've laid out, this suggests that DATATYPE! and TYPESET! would need to evaluate to isotopes of themselves :-/

We could instead solve this by passing functions, e.g. generated by POINTFREE:

>> find [1 2 "abc" 3 4] (<- match any-string!)
== ["abc" 3 4]

I might suggest investing in doing this with functions, because isotopes really do have a cost. Sometimes we see big gains out of them and they are worth it. But TYPESET! isotopes, I'm not as sure. Maybe let the practice with the others settle down first.