Why are chain parameters hardcoded rather than defined in a configuration file? I'm interested in running a private test network to experiment with various chain parameters. These are hardcoded in chainparams.cpp.

https://github.com/bitcoin/bitcoin/blob/master/src/chainparams.cpp

Unfortunately, this means that to run any experiments, I need to recompile Bitcoin Core.

What I'd like to do instead is simply edit a configuration file, then restart Bitcoin Core.

Why are chain parameters hard coded this way? Is there any way to inject my own chain parameters through a configuration file without recompiling Bitcoin Core?
I don't think there's really any better reason than "Satoshi Nakamoto chose to write the code that way, and nobody ever changed it".

There's no inherent reason why these values couldn't be loaded from a configuration file, but there's a general principle that config files are intended for parameters that are user configurable.  A Bitcoin user couldn't change anything in that config file or their client would quit working with the rest of the network.  Developers might want to, but they are expected to be comfortable with recompiling.  So presumably nobody thought it was worthwhile to write the code to parse a config file for these values.

So if you want to be able to modify those values by editing a config file, you'll first have to write the code to parse and load the config file.  Which, of course, also involves recompiling (and a lot more work besides).  Perhaps now you can see why nobody has wanted to do it so far.  But if you do, you can offer the code back to the main Bitcoin Core development line, and maybe it will be incorporated, thus saving someone in the future from this quandary.

Recompiling Bitcoin Core is not all that much work, once you have a development environment and necessary dependencies installed.  And when you have compiled it once, changes to the chain parameters should only require you to recompile one file and relink (which Make will do automatically).