First Step of Mission Accomplished: we can convert actions to frames and back without any allocations! (Another good side-effect: object representations are now about 5/8 the size of how big they are in R3-Alpha....)
The frame you get has the names as the keys, with typesets as the values:
>> interface: as frame! :append
>> find interface/series integer!
; null (because APPEND doesn't allow INTEGER! in its series argument)
>> find interface/series block!
== #[true]
>> append-alias: as action! :interface
== #[action! [series value /part /only /dup /line]]
>> append-alias [a b c] 'd
== [a b c d]
Of course if you try to look at this frame, it will be hideous, because typesets are hideous. Things like ANY-VALUE! mold out as every single type.
What you would want (and what HELP wants) is something that looks much more like what you typed at source level. Plus, as this thread brings up, you want to be able to know more than just the types...you want to know if the parameter is quoted or not...if it's endable, if it's const, if it's a refinement, etc. etc.
This is About More Than Just Knowing...
Clearly it's useful to have a representation of an action, where the keys are parameter names and the values are parameter types/modes.
But besides just getting an immutable description of an action as a frame, we'd like to be able to get a mutable copy...tweak it, and make a new action!
So imagine:
>> f: copy as frame! :append
>> f/value: make typeset! [integer!]
>> append-only-integers: as action! f
That is actually pretty close to working. But we want to be able to change more than just the types accepted...we'd like to change the quoting convention, and whether something is a refinement or not.
As this thread's goal states: we need to find a way of packing all that information up into a value...the value that you get from this mapping in the context.
Big Picture: Building Actions From Scratch
This direction is all part of the vision laid out in "Seeing All ACTION!s As Variadic FRAME!-makers", which was written almost exactly two years ago.
That was before things like AUGMENT existed. But we can now see operations like AUGMENT as being something that comes from being able to append new fields to a copy of a frame, and then as action! on the bigger frame.
I wondered in the original post "where does the body go in the frame spec?" But I think the answer is now that it doesn't go anywhere... you just ADAPT code into the frame.
Plus, I think that you should be able to give frames to ADAPT...and when you do, that means the bound code will see the locals in the frame.
foo: func [x [integer!] <local> y] [...]
bar: adapt :foo [... this would bind to x, not y ...]
f1: make frame :foo
baz: adapt f1 [... again just see x, not y ...]
f2: make frame! [x: [integer!] y: _] ; or whatever notation for "local" is
mumble: adapt f2 [... this would bind to x *and* y ...]
The concept here is that once you seal something up as an action, the locals are no longer part of the interface...so anyone building on top of it can't see them. But when you're making an action from scratch, you can set up a frame with locals and adapt code into it that sees those locals.
So basically, when you first MAKE FRAME! on a BLOCK!, you get something that has no implementation. If you were to DO it, it would error... because what the fields contain are parameter descriptions and not intended parameter values. But then you'd ADAPT it to give it a body.
This should make it so that writing your own FUNC-like generator, or your own SPECIALIZE or AUGMENT or other concept... will be very easy!