Hence "ideally"
rr lets you call program functions just like gdb does (using the same gdb commands, in fact). The limitation is that any state changed by those functions is simply dropped when the function ends. It's still super-useful for dumping program state etc.
Pernosco supports this too.
Executing arbitrary code in the program's context is also a regular debug feature, but I don't think many reversible debuggers allow this.