Of course it’s hard, but nonetheless, it’s possible, and sometimes (unfortunately) necessary, because GHC is not a Sufficiently Smart Compiler, and the imperative von Neumann stuff bleeds through the functional abstraction sometimes. However, IME it is (usually) still easier than the other way around.
A point worth mentioning in this context however is that when you do that, you are probably no longer analyzing the program as an implementation-agnostic Haskell program, you are looking at it as a Haskell program as GHC would compile it and the GHC RTS would execute it - given a different (but compliant) Haskell compiler, the imperative interpretation of the declarative code might look different, due to the compiler taking a different execution strategy. But in practice, that’s OK, because the main reason for putting on the imperative goggles is usually because you want to learn about the program’s performance (memory usage, execution time, …), and that is going to be highly dependent on the choices a specific compiler makes.
Oh, I have nothing but respect for those machines of yore and the brave souls who made them do incredible things.
Well, but that depends on the definition of “sequence” and “statement”, doesn’t it.
Pretty much every programming language has some way of expressing actions to be executed sequentially - in a typical imperative language, that way is ubiquitous and used for almost everything, while in a functional language like Haskell, it is presented as an emergent property of certain constructs like monadic binds, function composition, lists, etc., and of course changing the order of those things does change the program’s behavior. [1,2,3]
and [2,3,1]
are clearly not the same program.
This is why I think it’s important to differentiate the notion of imperative code from that of an imperative language. I can write imperative code in Haskell, I can write (somewhat) functional code in JavaScript, but I would not usually call Haskell an “imperative programming language”, or JavaScript a “functional programming language”, because while it is possible to write in the respective idiom in either language, it isn’t obvious, idiomatic, or particularly easy.