ARRAY/INITIAL and ACTION! Isotopes

So the ARRAY mezzanine from R3-Alpha allowed you to make an array with an initial value that could be a function...in which case it evaluated that function each time:

r3-alpha>> array 3
== [none none none]

r3-alpha>> array/initial 3 10
== [10 10 10]

r3-alpha>> n: 0 array/initial 3 does [n: n + 1]
== [1 2 3]

This raises the question of how you would make an array whose initial value was itself a function.

If you're a loophole-minded person, you might notice you could trick this particular case by making a function that returns a function.

r3-alpha>> array/initial 3 does [:append]
== [#[action! [...]] #[action! [...]] #[action! [...]]]

But that's not really a general answer for how to deal with this kind of polymorphism--it just works if you're using the result purely to do a substitution.

Trying This With Isotopic ACTION!s

I've loosened my stance on isotopes in function frames as parameters. It's not a good idea to take them as default, but forcing you to make every parameter ^META just to get isotopes is limiting.

So I gave this a shot. The notation I've got for now is that something like [~action!~] in a typeset says that parameter is willing to accept action isotopes.

array: func [
    {Makes and initializes a block of a given size}

    return: "Generated block or null if blank input"
        [block!]
    size "Size or block of sizes for each dimension"
        [<maybe> integer! block!]
    /initial "Initial value (will be called each time if action isotope)"
        [any-value! ~action!~]
]

So that [any-value! ~action!~] leads to a willingness to accept function isotopes. Having such an annotation leads you into the "danger zone" where if you don't prefix accesses to INITIAL with a colon, you run the risk of running a function. But if you don't have the annotation, you don't have to be paranoid and decorate your references.

Here's how the INITIAL references wind up in the implementation:

case [
    block? rest [
        repeat size [append block (array/initial rest :initial)]
    ]
    activation? :initial [
        repeat size [append block run :initial]  ; Called every time
    ]
    any-series? initial [
        repeat size [append block (copy/deep initial)]
    ]
] else [
    append/dup block initial size
]

So the BLOCK! case which recurses uses the :INITIAL for pass-thru.

I used RUN :INITIAL when I could have just used INITIAL, simply because it feels more clear. If this wasn't already contained by being at the end of a block, it would be better to limit the parameterization by saying something like apply :initial [] or the now-equivalent initial/ []

Since the only isotope type in the typecheck is action, there's no need to use the leading colon for the other references.

And It Works

What we're kind of accepting as a default is that function generation produces isotopes. So getting a plain action requires some kind of extra step, like REIFY

>> func [x] []
== ~#[action! [x]]~  ; isotope

>> action: reify func [x] []
== #[action! {action} [x]]

>> action  ; not word!-active
== #[action! {action} [x]]

>> ap: reify :append
== #[action! {ap} [series value /part /dup /line]]

>> ap
== #[action! {ap} [series value /part /dup /line]]

What this means is that you get a similar behavior to before, where if you pass a "live" function you've fetched from a word or just generated, then it runs. You have to do something extra to it to get it to be inert.

>> array 3
== [_ _ _]

>> array/initial 3 10
== [10 10 10]

>> n: 0 array/initial 3 does [n: n + 1]
== [1 2 3]

>> array/initial 3 reify does [n: n + 1]
== [#[action! []] #[action! []] #[action! []]]

That Looks Like A Polymorphism Success Story...

It doesn't require any convoluted thinking to get the literal vs. non-literal distinction. And you can apply this technique to cases that don't have a weird workaround.

And of course what I really like here is how you have to explicitly indicate you tolerate function parameters that will execute if you don't use a GET-WORD! access.

I wish I could say that all of ACTION! isotopes impacts were as well sorted out as this looks. But it's a start, and it shows the potential.

2 Likes