I have a data type data Foo a b = Bar a b
that I use internally in a library.
I also have an alias for one of its more common concrete forms: type
You can export Bar
type simply by module Quux (Bar) where ...
, though you will not be able to construct any Bar
s.
If you also need the constructor, you may use similar technique to smart constructors, that is create a helper functions which creates Bar
s and export that one:
module Quux (Bar, bar) where
data Foo a b = Foo a b
type Bar = Foo Int Int
bar :: Int -> Int -> Bar
bar = Foo
then
\> let b = bar 1 2
\> :type b
b :: Bar
\> let f = Foo 1 2
<interactive>:9:9: Not in scope: data constructor ‘Foo’
Why do you want to do this? No, don't bother answering. Don't do this. Whatever you think it will accomplish, it will not. What you can do, however, is use a newtype (note that I changed the names a bit):
newtype Bar = _Bar (Foo Int Int)
data Foo a b = Foo a b
Now you can use pattern synonyms to make Bar
user-friendly:
{-# LANGUAGE PatternSynonyms #-}
module Whatever (Bar, pattern Bar)
pattern Bar a b = _Bar (Foo a b)
There's a bit of weirdness having to use the pattern
keyword to import the synonym, but oh well. Unlike your approach, the end user has access to a proper first-class type and not just a synonym. And they can't see anything of Foo
.
This isn't possible in Haskell 2010, but is possible in GHC.
In Haskell 2010, you can only export constructors as part of a data type:
Data constructors cannot be named in export lists except as subordinate names [the
Cᵢ
inT(C₁,C₂)
], because they cannot otherwise be distinguished from type constructors. [Haskell 2010 Report, §5.2 "Export Lists", #2]
In GHC (version 7.8 or later), however, you can use the PatternSynonyms
language extension to accomplish this: with that turned on, you can qualify constructors in export lists with pattern
. So, for instance, your desired example would be
{-# LANGUAGE PatternSynonyms #-}
module Quux (Bar, pattern Bar) where
data Foo a b = Bar a b
type Bar = Foo Int Int
The pattern Bar
in the export list specifies the constructor, and the unadorned Bar
specifies the type synonym.
In addition, if you think the unadorned Bar
is confusing/ambiguous, you can use the ExplicitNamespaces
extension (in version 7.6 or later) to enable prefixing type constructors with type
, similarly:
{-# LANGUAGE ExplicitNamespaces, PatternSynonyms #-}
module Quux (type Bar, pattern Bar) where
data Foo a b = Bar a b
type Bar = Foo Int Int
From the documentation, about exporting constructors with pattern
:
[W]ith
-XPatternSynonyms
you can prefix the name of a data constructor in an import or export list with the keywordpattern
, to allow the import or export of a data constructor without its parent type constructor [GHC 7.10 Users Manual, §7.3.26.4 "Explicit namespaces in import/export"]
and
You may also use the
pattern
keyword in an import/export specification to import or export an ordinary data constructor. For example:import Data.Maybe( pattern Just )
would bring into scope the data constructor
Just
from theMaybe
type, without also bringing the type constructorMaybe
into scope. [GHC 7.10 Users Manual, §7.3.9.2 "Import and export of pattern synonyms"]
Plus, for exporting types with type
:
The
-XExplicitNamespaces
extension allows you to prefix the name of a type constructor in an import or export list with "type
" to disambiguate… [GHC 7.10 Users Manual, §7.3.26.4 "Explicit namespaces in import/export"]
That said, I am inclined to agree with dfeuer here – there's a reason the report disallows this. Type signatures that are impossible to write down – e.g., Bar :: a -> b -> Quux.Foo a b
– are a bit maddening. But the type synonym does help with that; just make sure your documentation is thorough :-)