ENSURE vs. ENFORCE

I noticed that I have two different ways of using the word "Ensure".

The first meaning is "If something hasn't already been done, make sure it gets done." This happens with things like passing a series to a value cell...and anything that gets put into a value cell has to be under the control of the memory manager. That's indicated by a bit set on the series, so Ensure_Series_Managed(series) will check to see if the bit is set, and if so it is a no-op. If it's not set, then some code runs and the bit gets set.

The second meaning is "Check if something has a property...and pass the value thru if it has that property, otherwise trigger a failure and interrupt the operation." This is how it's been used in the ENSURE native, where you give it a datatype and can say something like ensure integer! foo. Inside the C code I can use it the same way, e.g.

REBVAL *v = ENSURE(REB_INTEGER, First_Value_In_Array(array));

I'm not thrilled about having the word mean both things. At first I figured that ENFORCE might be used for the first case...but then it sounds like what it does is more severe. Depending on how you look at it, "forcibly" changing a condition to be true even if it was not might be more "intense" than raising an error...

ENSURE has precedent in design-by-contract, which suggests failure vs. massaging things to be how you want:

That goes along with me thinking I think I like the ENSURE native how it is, so it's the other word I want to change...currently it's only inside the implementation so it's not a big issue. But talking it through in case anyone has ideas.

; could this better suggest that if the array is not managed, it becomes so?
; there is no error, only bringing the argument into compliance.
; also should suggest that the input value is passed through either way.
;
Init_Block(value, Enforce_Array_Managed(array));

Perhaps just "FORCE", instead of "ENFORCE"?

Init_Block(value, Force_Array_Managed(array));

Hmm... I think I like that.

It could be used as a Redbol function in a context where you want to do a TO conversion, but only if something wasn't the type already:

>> blk: [a b]
>> forced: force block! blk
== [a b]
>> append blk 'c
>> forced
== [a b c]  ; kept identity; was passed thru as-is

>> int: 1
>> forced: force block! int
== [1]  ; had to have a new thing made for it to be blocklike

This is the spirit of the kinds of functions I'm talking about...to have a baseline case where they are no-ops, but then take action if the thing they want isn't true enough to pass through a result with that property. Previously I called the above force block! concept "blockify", but this could generalize better and be less of a weird name.

1 Like
>> blk: [a b]
>> forced: force block! blk
== [a b]
>> append blk 'c
>> forced
== [a b c]  ; kept identity; was passed thru as-is

>> int: 1
>> forced: force block! int
== [1]  ; had to have a new thing made for it to be blocklike

I forgot I'd come up with this. It's a little wordier than BLOCKIFY... but not as wishy-washy a term. I guess the thing is that it's hard to tell whether force block! '(a b c) would be [a b c] or [(a b c)]...though not any harder to tell than BLOCKIFY. :frowning:

Another Term: KNOWN

Known is something that I've been working with, that means "I'm absolutely sure". It's like an assertion...but you don't want it to have any cost (in the release build, at least).

With the C++ build we can do things like statically analyze to make sure something is true, and fail the compiler detects what you thought was known was false. The debug build can have an assert that's not there in the release build.

The only analogue in Rebol interpreter land would be to say "Here's something I know, but I don't have the code to verify it (or don't want to pay to check it)"

do-crypto-thing (known <prime> p)

I've mixed up ensure() and known() a bit in the C++ code but I think I can untangle that... if it's not doing any runtime checks in the release build, it should be using the word "known".

2 Likes