Imagine there is a newtype declaration:
newtype T = T Int
This declaration is in a module but not exported. I would like to pass a value of type T to a function. Now, I can declare my own version of T using the exact same definition. The compiler will of course complain if I pass (my.T 0)
to a function expecting (hidden.T 0)
. I will use unsafeCoerce to coerce the former to the latter. It's mentioned here that usage is safe "between a newtype and the type that it wraps." I would like to just check if it's also safe in the case I described.
I know this is frowned upon and against all principles of good software practices, type theory, functional programming philosophy, ghc policies, common sense,..etc. Yet, I want to know if this will "normally" work.
This might be safe with the current GHC implementation, but this is not the recommended way to solve your particular problem.
The pattern that is usually used instead is to have an internal module like this:
module Foo.Types (T(..)) where
newtype T = T Int
This module is declared as un-exported in your Cabal file. Then, in the module where you want to use the type in, you import the Types
module, and use the constructor directly:
module Foo.Bla where
import Foo.Types (T(..))
f :: T -> Bla
f (T int) = makeBla int
Finally, you can export the opaque type however you want. For example:
module Foo (T) where
import Foo.Types (T(..))
makeT :: Int -> T
makeT = T
It is possible to use coercion instead, but it would be a bad idea to rely on 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