I wanted to package up the parameter typechecking functionality into a TYPECHECK function that you could use:
>> typecheck [integer!] 1020
== \~okay~\ ; antiform
>> typecheck [integer!] <foo>
== \~null~\ ; antiform
I decided that it should probably pre-decay values by default... just like a normal parameter would. So a pack with two integers in it would look like an integer!, not a pack:
>> typecheck [integer!] pack [10 20]
== \~okay~\ ; antiform
>> typecheck [pack!] pack [10 20]
== \~null~\ ; antiform
This corresponds to the behavior of a non-^META argument when used as a parameter. It can't receive packs, so if your typecheck incidentally allows packs that's irrelevant. Instead you have to use a ^META parameter convention:
>> typecheck:meta [pack!] pack [10 20]
== \~okay~\ ; antiform
But ^META parameters are able to decay to "coerce" to the parameter requirement, so integer! would still work if you used the :META refinement:
>> typecheck:meta [integer!] pack [10 20]
== \~okay~\ ; antiform
Okay, But What About ERROR! Values?
It is relatively quite rare that you intend a type checking operation to pass through an unstable ERROR! antiform as if it was intentional.
So you certainly need to specify a :META check. But that shouldn't really be enough:
e.g. this seems like a dangerous answer to me:
>> typecheck:meta [integer! pack?] 1 / 0
== \~null~\ ; antiform
It would be one thing if ERROR! was specifically in the typeset, but if it's not, that's likely going to allow accidents to pass through.
There could be a :RELAX
refinement. But I had a different idea... what if it just passed the ERROR! through, as the result of the check?
>> typecheck:meta [integer! pack?] 1 / 0
** Error: Divide by Zero
>> (typecheck:meta [integer! pack?] 1 / 0) except e -> [e.id = 'zero-divide]
== \~okay~\ ; antiform
>> try typecheck:meta [integer! pack?] 1 / 0
== \~null~\ ; antiform
That's strictly more powerful than a :RELAX
refinement, and looks a little nicer and briefer.
This Got Me Thinking, What If More Type Checks Did This?
Today, many type checking functions pre-decay their inputs, so errors are promoted to panics:
>> integer? 1 / 0
** PANIC: Divide by Zero
But some of the ^META-oriented functions are not as finicky; they take their arguments undecayed, and then don't bother to report ERROR! as a problem:
>> pack? 1 / 0
== ~null~ ; anti
You can imagine people wanting it either way...maybe they want to gloss over ERROR!, and maybe they don't.
The same pattern could apply here. Pass through the ERROR!. People can say try pack?
if they want errors to count as not passing the check, otherwise if there's no triage with EXCEPT or another routine then it will be promoted to a panic.