I'm using a package that carries around things in a convenient ReaderT monad. Their runReaderT/unwrapping function is in a hidden module. I'd like to be able to unwrap it arbitrariy/manually with an arbitrary environment. So I don't have any qualms with doing it manually -- however, the environment type has a hidden data constructor, so I can't generate an environment that will typecheck with runReaderT.
Is there any way I can get around this? Or should I give this up as impossible and attempt to restructure my program? I'm planning on deploying this project to a remote system with an automated build system so I'm not sure if I want to edit the source of the package directly/fork it.
In specific, I'm using scotty
, and I'm trying to emulate runActionM
from the Web.Scotty.Action
module. I trying to emulate a page request and capture the results, and also to emulate a page request that normally has IO side effects.
Specific example:
-- LibraryPackage.hs
module LibraryPackage (SpecialReader) where
import Control.Monad.Reader
type SpecialReader a = Reader Env a
data Env = Env Int
runSpecialReader r = runReader r (Env 5)
-- Main.hs
import LibraryPackage
main = do
let
r = return 10 :: SpecialReader Int
-- problem here -- cannot construct an `Env`
print $ runReader r (Env 5)
You're pretty well and truly stuck. Here are the options
undefined
if you don't actually need meaningful valuesunsafeCoerce
Haskell doesn't let you do things like creating types your not supposed to. Imagine what cause there would be if you could pattern match on IO
for example.
Many packages use the module system to provide safety guarantees or optimizations that would be impossible if the hidden objects were able to be used in arbitrary ways by client code (the most obvious example being IO
!). The module system is designed to make it impossible to do what you're trying to do.
However, many packages also have "secret" exposed modules (often with Internal
in the name) that export the hidden names anyway. These often won't show up in the official docs, but you'll see them in the source/cabal files.
If Scotty is such a package then you're in luck, but using an internal module is tricky. Treat them like the unsafe*
family of functions; in general they cause things to go horribly wrong, and the onus is on you to make sure it won't in this particular case. After all, the constructor for Env
may be deliberately hidden specifically to stop you running the monad stack on arbitrary environments because that would go wrong. Also, the package author is likely to not consider breaking changes to the internal module to be "backwards incompatible", so you may have to do forward ports of your code even on minor version updates to Scotty.
If there really is no module exporting the required constructor at all (not even a unadvertised one), then there's no way to get at it without changing the package's source code. You can ask for it to be exposed (publicly or as a back door), or fork the package, or try to redesign your code so you don't need it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With