I tend to agree that this sort of logic ought to be pushed out to the optimizer, keeping the initial generation simple.
In the case you've highlighted, specifically Project must be allowed to take complex columns. I think this is fine: we can have an optimizer rule that takes a Project with complex columns and gives you a Project[Apply[]]