Tricks for debugging type alignment issues related to monad stacks

Haskell is big and beautiful, and I’m still making my way through the more advanced areas of this experience. Depending on the situation, the code I’m working with is usually (mostly) within my understanding and otherwise managable, in which case getting stuck or blocked is pretty quickly resolved. And I tend to stay out of the stuff that’s way over my head, or at least go into that with a mentor close by (someone to help light up the dark room I might have ended up in).

Then, there are the times when I’m between those two, and the code I’m working with is mostly within my grasp, but includes some piece that is still outside my understanding or mastery of that topic.

In the past, to still be productive in those cases, I would find ways to manipulate the code to eventually find a way to appease GHC. I’m sure you’ve had those similar “wow, that compiled” moments (and it works as expected!), after struggling with a very similar incantation of the code that you’ve somehow figured out how to shuffle around, and you aren’t quite sure why that small shuffle worked in this case. Over time, working through those, it makes more sense.

The funny part about this is that none of the typical learning materials are all that useful. You can read them, and possibly even so some exercise, but it’s too trivial to apply or have any real meaning in the real-world scenario you have.

The help of a mentor is really effective in those moments, but not always available. To make do, we find some tricks, “maybe I should do X”, for various situations. The ability to use these would preceed my full understanding of why I would need that X, but in time it would make more sense. I used to throw pack and unpack around in embarrassing ways. I still use pure () when I don’t know why it is needed (other than “to make GHC happy”).

My question is about these types of tricks (un/liftIO, type holes, shuffling code, changing code structre, using where, etc) and debugging the type alignment issues that we sometimes run into when working with monad stacks.

Do you have tricks you recommend, or find useful, when stuck on some monad stack with types not aligning the way you expect?

Maybe there is a list of topics I should focus more energy on mastering, and which would make this easier?