Can I export constructors along with a type alias?

前端 未结 3 1070
天命终不由人
天命终不由人 2021-01-13 15:06

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

相关标签:
3条回答
  • 2021-01-13 15:38

    You can export Bar type simply by module Quux (Bar) where ..., though you will not be able to construct any Bars.

    If you also need the constructor, you may use similar technique to smart constructors, that is create a helper functions which creates Bars 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’
    
    0 讨论(0)
  • 2021-01-13 15:48

    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.

    0 讨论(0)
  • 2021-01-13 15:57

    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ᵢ in T(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 keyword pattern, 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 the Maybe type, without also bringing the type constructor Maybe 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 :-)

    0 讨论(0)
提交回复
热议问题