Can `TAKE []` Just Return Null?

Here is the behavior of TAKE in Rebol2:

rebol2>> block: compose [1020 (none)]
== [1020 none]

rebol2>> take block
== 1020

rebol2>> take block
== none

 rebol2>> take block
 == none

 rebol2>> take block
 == none

One thing I was bothered about was the inability to notice a difference between taking from an empty block and taking from a block containing a NONE!. Ren-C can resolve that with NULL as a unique response to taking from a block with no elements in it.

But there's another concern which BrianH expressed, about a routine that you expected to have a side effect that did not. If you write a line simply like take block and it doesn't take anything away, should it be silent about that fact?

Early Ren-C introduced TAKE* as distinct from TAKE...

Experiments at being prescriptive worried that getting back a NULL might not be enough to warn you about a misunderstanding. So we wound up with this:

ren-c>> take []
** Error: TAKE-ing from an empty block, use TAKE*

ren-c>> take* []
; null

To make things simple, TAKE was just a chained function derived from the lower-level TAKE*. This seemed "safer"...at the time, and addressed BrianH's (pre-NULL) concern...as well as my general overarching desire to use things that are "more checked".

But this may be the wrong place to get paranoid about the user's intent, and we should just be happy that the NULL offers a unique result that means nothing was taken.

When I wrote about "Make Your Own Safety" I asked what the odds were that we could guess the top pet-peeve that every programmer would have.

How many times have you been bitten by TAKE on an empty block you didn't know was empty? When you did, how likely was it that you blamed yourself vs. the tool?

Not only that, but what if you could get the "hardened" versions of TAKE and TAKE* in the files where it was bothering you as easily as:

 take*: :lib/take
 take: (=> non null lib/take)

This seems the better angle, and avoids making it feel like there's any kind of bias toward the "tics" that bother @rgchris, which the * may represent. Give people the power to bend fast to address the specific concerns that are bothering them with the bent and personalized/customized code they are writing to solve the problem they have right now.

I don't think I have been bitten by that. For my personal preference, having TAKE return NULL from an empty block seems better than throwing an error-- the latter behavior implies that the block! cannot be-empty, which, I don't know, seems un-block-like. Using TAKE then makes a block! less block-like. I can think of all kinds of interesting variants of block! structures (constrained sizes, LIFO/FIFO) which could be convenient and safer for some TAKE scenarios.

I think that this is a good example of something that fits in well with TRY, under the new definitional-error-powered conception:

>> block: []

>> take block
** Error: TAKE-ing from an empty block, if intentional use TRY TAKE

>> try take block
; null
2 Likes