The gen_server itself passes its module name to sys:handle_system_msg[2] when it receives a system message like a code-change notification, which ends up being the Module:Function type of call that always refers to the new version of the module, so it always ends up running the latest version when you upgrade gen_server.
[1] http://www.erlang.org/doc/man/gen_server.html#Module:init-1
[2]file:///C:/Program%20Files/erl5.9.1/lib/stdlib-1.18.1/doc/html/sys.html#handle_system_msg-6
On the code state upgrade side there are usually a few different issues that can arise and I'd be very curious to hear if there are ways Haskell would handle some of these.
The first one is type changes. I might have a record that has a new field added. Now it's not necessarily pretty to upgrade on call with a pattern match or using a code upgrade protocol but it's easily expressed dynamically.
Another is in the interface, like adding new arguments or changing from a synchronous call to an asynchronous one. These are a bit easier to handle via indirection though they show that you'll need to plan your entry/exit points for upgrades carefully (again, OTP has things like gen_server which make this much easier).
If Haskell can manage to get past they type boundary issue then it's really a matter of supporting at least 2 simultaneous versions of code so each process can be scheduled and upgraded in natural course. Handling more than 2 could be of use depending on how aggressively you want to purge, for example, a local rather than fully qualified call can be caught in a closure and passed around as in some value to be called later. These long lived references will need to be handled carefully or you might get some delayed surprises.