I was thinking more of 1 UDT per function that is custom to THAT function. Create a heap-item for that function, then have your code blocks for that one function reference that specific function's heap-item.
To relate this to an actual (built-in) example, the HSIO module does multiple functions. Some are input, some are output, some are both.
There is an Axis Struct with 4 heap-items of that Axis Struct type (e.g. $Axis0, $Axis1, etc.). Axis Struct contains members like .CurrentVelocity .Direction et.al.
Then there's PWMOut Struct related to the Pulse Width Modulation Output functions. There are up to 4 of those (you have to instantiate them individually). That structure has members like .DutyCycle and .EnableOutput
So, what are your set of functions? Is there a different set of I/O and internal states for each function? Can you have multiple instances of a specific function across different I/O sets (similar to HSIO PWMOut and Axis structures can have up to 4 instances of each one)?
Not all code-blocks would reference all the different heap-items. The ones related to "Function A" would use FuncAStruct and heap item FuncA (e.g. FuncA.Output and FuncA.Input - I have no idea what you would call your members). The ones related to "Function B" would use FuncBStruct and heap-item FuncB (e.g. FuncB.Speed and FuncB.MotorOn, whatever).
Then at the $tTopOfScan, you would move X50 into the specific current selected function's heap-item. This would be long, moving X50 into FuncA.Input, but only if FuncA is the current function. Similarly, in $tBottomOfScan, you would move FuncA.Output into Y43 if FuncA is currently selected, but move FuncB.MotorOn into Y43 if FuncB is currently selected.
This is closer to Object Oriented programming where specific code-blocks are tightly coupled to a specific UDT/heap-item, and you do the "translation layer" in the $tTopOfScan and $tBottomOfScan system tasks.