Typechecking Quoted forms / Quasiforms / Antiforms

Asking questions about antiforms can be a bit tricky.

If you have a meta result that contains something that might be a pack, you'd have to say:

if pack? unmeta result' [...]

But you're just asking a question. What if it contained a meta of a raised error? Should PACK? be willing to accept raised errors? If not, that would cause an abrupt failure.

To dodge the need to do an UNMETA, you could say:

all [
    quasiform? result'
    block! = heart of result'
] then [
   ...
]

And via such a mechanism, you could write a function META-PACK? that would not fail on META-RAISED:

if meta-pack? result' [...]

That has the nice feature of not forcing you to perform an UNMETA just to answer the question. You can narrowly ask "is this a meta pack".

But I think such things need to be in the box. This question gets asked a lot, and I'm tired of asking it as the ugly and breakable pack? unmeta.

What If Typecheck Functions Had a :META refinement...?

It doesn't seem like clogging up the namespace with more functions makes a lot of sense, so we might offer a refinement:

if pack?/meta result' [...]

That puts some separation between the ? and the thing you're testing. It's a little less severe with the new format using a CHAIN!:

if pack?:meta result' [...]

We could put a question mark on the refinement as well :face_with_diagonal_mouth:

if pack?:meta? result' [...]

Okay, no, we won't do that.

I kind of feel like putting the meta after makes it unclear. You'd read it like pack? meta result'. Does that mean it should just be UNMETA?

if pack?:unmeta result' [...]

But at that point, people are probably not going to generally know what the difference is between that and pack? unmeta result

None of this is as good as simply meta-pack?. But we don't want to generate new words for every possible thing (and questions like meta-integer? are equally legitimate...)

What About Arbitrary CHAIN! Interpretation?

Let's forget for a moment about the existing refinements to META and QUASI. What if they--as functions--were willing to take other functions as a refinement?

if meta:pack? result' [...]

This could be a general pattern of solution...

if (quasi:group? first [~(a b c)~]) [...]

if (quote:word? first ['a b c:]) [...]  ; or quoted:word?

How would it work? Well, I guess the basic idea would be that it would do a processing step on the argument before typechecking it and passing it to the constraint function.

So quoted:word? would run QUOTED, and it would be able to inspect the chain as containing WORD?.

  • If it was passed something like <foo> it would say "I don't even have to ask, that's not a quoted anything".

  • If it was something like '<foo> it would say "okay that's quoted, I need to ask" and it would unquote the thing before passing it to WORD?, which would come back false.

  • If it was 'bar it would do the same thing but this time WORD? would return true.

It feels a little bit ad-hoc, but the ergonomics are certainly there. This would be tremendously useful and could create new and useful type constraints. It's literate... and meta:pack? is going to look better in type specs than meta/pack?

Not only that, but recognizing "intrinsics" in the chain would mean this could be relatively fast--faster than pack? unmeta could be.

What About Refinements Like META:LITE

Let's say there's a single-arity logic-bearing function someone makes called LITE, and they get mad that META:LITE doesn't call it to do a meta version of that test.

I don't know that I care a ton about that. We have situations in PARSE where you might have a variable named ACROSS and it will ignore that variable if there's a keyword called ACROSS. You have to escape your words when they run up against reserved dialect words.

So I guess you'd wind up with something like:

 if meta:(lite/) [...]

Otherwise the keyword would win.

What About Cascading The Functions?

if (quoted:quoted:quasi:block? first [''~[a b c]~]) [...]

Well I guess that could work if the rule was that if you used a function name as a refinement, that function would receive all the refinements after it. Applying that recursively you'd get something that works.

There could be an optimization that functions like QUOTED would detect when the functions in the chain were ones they recognized, and they could do something optimal.

Does Any Of This Solve The UNMETA Problem?

It's nice syntax, but remember the original issue...where you have a meta-raised-error, and you want to test it to see if it is or isn't a meta pack:

if pack? unmeta result' [...]

I'm talking about redoing that:

if meta:pack? result' [...]

But if META is unmeta'ing its argument, and passing it to PACK?, does that still have the same problem?

It doesn't have to. META would see that the result is a quasiform, hence a candidate for any function to ask... then it needs to typecheck against what PACK? takes. But if that typecheck fails, there's no need to raise the error. You can just say "no, it's not a meta pack". Done.

Are There Less Wacky Ways To Do It?

I dunno. It's admittedly a bit weird to put that much overloading on words like META and QUASI. Their type signatures stop making sense (returning LOGIC?...sometimes?)

The overloading could be reduced if the weird modifiers were QUOTED, METAD, and QUASID. :-/

if metad:pack? result' [...]

Errr, no. Didn't come this far to write things that look like garbage.

But oddly, quoted:xxx? looks better than quote:xxx?... does that suggest the operator should be called QUOTED too? :thinking:

>> quoted 1 + 2
== '3

I've seen it as QUOTE for so long that it's hard to frame it. It doesn't look terrible, and when you put them side by side the brevity has an advantage:

if (quoted:quoted:quasi:block? first [''~[a b c]~]) [...]

if (quote:quote:quasi:block? first [''~[a b c]~]) [...]

To say that 'a is a QUOTE-WORD! is a little bit caveman-ish, but better than calling it a LIT-WORD!, and it takes one less syllable to say "is a quote word" than to say "is a quote-uhd word".

Looking at it in that light, it's growing on me to dumb it down. Maybe not in all circumstances. Perhaps it could be left as a matter of taste with QUOTED and QUOTE being synonyms... like FUNC and FUNCTION, use whichever you want.

I Think This Is A Direction Worth Pursuing

I've been looking for an answer to this question, and haven't thought of anything this good before.

Don't have a complete design for the "customized refinement dialect", but I can do some prototyping...

I started writing and just about forgot to talk about the example that motivated me to start talking about it.

Where I was going to go with this was to talk about this example:

; ~<unreachable>~ antiform => "unreachable"

if tripwire? get/any $reason [
    reason: as text! unquasi ^reason
]

Imagine doing this more cleanly, as:

if meta:tripwire? ^reason [
    reason: as text! unquasi ^reason
]

Still a little bit laborious. Basically, once you have a value in a variable that you can't get through a normal reference, something has to step outside that.

If I were willing to let AS TEXT! take quasiforms, you could duck the unquasi:

if meta:tripwire? ^reason [
    reason: as text! ^reason
]

It may seem tempting, but I don't want to allow that.

But we don't have a single operation that fetches an ornery antiform and gets it to plain form in one step.

If we did have that operation as a function--call it flatget for a moment--it would still look like:

if meta:tripwire? ^reason [
    reason: as text! flatget $reason
]

Which isn't really any better than unquasi ^reason, and just injects a new term instead of using the sunk cost of vocabulary surrounding isotopes that we absolutely have to have.

Dealing with tripwires is just going to be a little difficult by design. At least the ability to go to the ^META level on both the retrieval and the test cleans it up a little.