After about two years on and off with Haskell, I'm still pretty much a beginner. Thinking about what slows me down most is the difficulty to understand data structures and monad stacks that result from using the various libraries needed for a production system. I often find myself in a situation where I would love to simply set a breakpoint somewhere and then inspect bindings and nested data structures at that point. Working with complex frameworks in other languages, this often provides much more insight into data and runtime behavior than studying their source code and (often inadequate) documentation. What would be a similar "explorative" route in Haskell?
Yes, this a rather imperative thinking, with code that pretty much equals control flow, and where bindings are variables with mutable state. Yet, working with complex monad stacks from other libraries, Haskell programs are not that different, I think.
For example: I'm trying to build a Servant application with sessions. In a Servant client, the session information definitively must be maintained somewhere in the stack. Is it Servant-Auth or at a lower level (Warp?) I'd like to know exactly where, and figure out how to access the session information. I'd like to write a test that performs a login, does some requests, and then logs out again.
In Python, Java, etc. I would do the login, break, and poke around in the in-memory data structures to figure out where the information resides; then compare with the documentation to understand how to best use it. In Haskell, I feel stuck wading through library source code and piecing together layers of type signatures, or even construct minimal example programs to understand what is going on. This is certainly ten to fifty times slower than simply inspecting what is already there in memory, but which I can't readily explore.
Thus, I think an "explorative" style to be helpful understanding complex libraries and frameworks, and to become productive quickly. Therefore, I'm looking for recommendations how to best explore Haskell code. Is there a way to use ghci for that, even with a complex application stack? The debugger? Any other suggestions, that provide more insight than sprinkling trace statements all over my code?