You still have your "setup method":
Signal.map show (Signal.foldp update initShip inputSignal)
and your "handle updates method": updateVelocity newVel (updateShooting isShooting (applyPhysics dt ship))
And the distinction between "set the ship's position to ship.position + ship.velocity * dt" and "create a new ship similar to the old ship but the velocity is ship.position + ship.velocity * dt" seems like splitting hairs.It's not OO, definitely—but it doesn't feel like you're doing anything differently than you would if this was a purely structural program, even if some of the details are different. If this was straight C, "update" would be a method that gets called 30 times per second, and "main" would get called when the game starts, and everything else would map pretty much line-for-line.
Similarly, the "functional-reactive" nature of it feel like an implementation detail, rather than a different way of thinking about the code—in your update method you still walk through the steps "was pressed? was ^ pressed? Move the ship. Fire your gun. Change your velocity." Maybe some of them don't need to be recalculated? Okay, but as the programmer you still need to describe the same steps, even if some of them get optimized away.
And honestly, even going through the same steps, it makes it harder to understand. Take your update function:
updateVelocity newVel (updateShooting isShooting (applyPhysics dt ship))
Okay, so you have an applyPhysics method that takes a ship, and a dt. That's pretty clear. And it returns... something, and that something gets passed into updateShooting, and then what updateShooting returns gets passed into updateVelocity. You have to go elsewhere to read that, okay, applyPhysics and updateShooting both return ships. The same steps, written in a more imperative syntax: applyPhysics(ship, dt);
updateShooting(ship, isShooting);
updateVelocity(ship, newVel);
Which (a) makes it more clear that a ship gets passed into each method, but (b) also lets you pass in the more important parameter first, which aids readability, and (c) lets you list the methods in the order that they occur, rather than writing them in the reverse of the order they occur. To at least get the better argument order with Elm you'd have to write it: updateVelocity (updateShooting (applyPhysics ship dt) isShooting) newVel
Which is completely unreadable—you're reduced to counting parentheses to see which method "isShooting" gets passed into.