I've got an internal module I'd like to provide an external API for
module Positive.Internal where
newtype Positive a = Positive { getPositive :: a }
deriving (Eq, Ord)
-- smart constructor
toPositive :: (Num a, Ord a) => a -> Maybe (Positive a)
toPositive a | a <= 0 = Nothing
| otherwise = Just $ Positive a
-- ...
I want to hide the dumb constructor, and replace it with a unidirectional pattern so users can still pattern match values, they just have to use the smart constructor to use new values.
Since I want the pattern and the dumb constructor to use the same name, I need to hide the dumb constructor to prevent namespace clashes.
However, since the dumb constructor and the type share names, it's a little tricky to import the everything BUT the dumb constructor.
Currently I'm doing this, which works ok:
{-# LANGUAGE PatternSynonyms #-}
module Positive
( module Positive.Internal, pattern Positive
) where
import Positive.Internal (Positive())
import Positive.Internal hiding (Positive)
import qualified Positive.Internal as Internal
pattern Positive :: a -> Positive a
pattern Positive a <- Internal.Positive a
I could simplify my imports by just using the qualified import, but I'm curious.
Is there a way to, in a single import statement, import all of Positive.Internal
except the dumb constructor?
I tried hiding (Positive(Positive))
, but that hid both the type and the dumb constructor. I've poked about the wiki, but I haven't noticed any way to differentiate between constructors and types in hiding
lists.
Correct me if I am wrong, but I am almost certain this is what you are looking for:
{-# LANGUAGE PatternSynonyms #-}
module Positive
( module Positive.Internal, pattern Positive, foo
) where
import Positive.Internal hiding (pattern Positive)
import qualified Positive.Internal as Internal (pattern Positive)
pattern Positive :: a -> Positive a
pattern Positive a <- Internal.Positive a
foo :: Positive Int
foo = Internal.Positive 5
Internal
module stays the same way as it is defined so far. And for the sake of example:
module Negative where
import Positive
bar :: Maybe Int
bar = getPositive <$> toPositive 6
Let's double check in GHCi:
Prelude> :load Negative
[1 of 3] Compiling Positive.Internal ( Positive/Internal.hs, interpreted )
[2 of 3] Compiling Positive ( Positive.hs, interpreted )
[3 of 3] Compiling Negative ( Negative.hs, interpreted )
Ok, modules loaded: Negative, Positive, Positive.Internal.
*Negative> bar
Just 6
*Negative> getPositive foo
5
*Negative> :i Positive
newtype Positive a = Positive.Internal.Positive {getPositive :: a}
-- Defined at Positive/Internal.hs:3:1
instance [safe] Ord a => Ord (Positive a)
-- Defined at Positive/Internal.hs:4:17
instance [safe] Eq a => Eq (Positive a)
-- Defined at Positive/Internal.hs:4:13
*Negative> :t Positive
<interactive>:1:1: error:
• non-bidirectional pattern synonym ‘Positive’ used in an expression
• In the expression: Positive
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