Wonderful question. I would love to see something like this too.
What kind of confusion are you running into with Haskell? Are they of the type error variety, or do you have a well-typed program that doesn't do what you want?
If it is the type error stuff, I would recommend that you give the compiler information about what you think the types are. The reason is that type information can flow from very far away places than where you are getting an error. For example, I was writing this code the other day:
for eIdpData \ $(schoolId, districtId, tenantId) -> pure $ Right 1
The type of for is for :: (Traversable t, Applicative f) => t a -> (a -> f b) -> f (t b)
In my case, I knew the following:
t ~ Either SyncFailure
a ~ IdpData
f ~ WriterT Logs m
b ~ Int
Once I do replace type variables with concrete types / type constructors, it becomes clear that my function argument should return an Int in some structure, and instead I am returning an Either a Int. This helps me figure out the problem.
Writing out takes a lot longer than actually doing it. Anyway, the key is to provide GHC with the information you think you know; this will prevent type inference from allowing type information to flow from far away places, and the error will become clearer or at the very least more localized.