【问题标题】:Hide a constructor but not the type on import隐藏构造函数,但不隐藏导入时的类型
【发布时间】:2017-04-25 15:08:29
【问题描述】:

我有一个内部模块,我想为其提供外部 API

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
-- ...

我想隐藏愚蠢的构造函数,并用单向的替换它 模式,因此用户仍然可以模式匹配值,他们只需要使用智能构造函数来使用新值。

由于我希望模式和哑构造函数使用相同的名称,我需要隐藏哑构造函数以防止命名空间冲突。

但是,由于哑构造函数和类型共享名称,因此导入除了哑构造函数之外的所有内容有点棘手。

目前我正在这样做,效果很好:

{-# 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

我可以只使用合格的导入来简化我的导入,但我很好奇。

有没有办法在单个导入语句中导入所有 Positive.Internal,除了哑构造函数?

我尝试了hiding (Positive(Positive)),但这隐藏了类型和愚蠢的构造函数。我已经讨论过the wiki,但我没有注意到有任何方法可以区分hiding 列表中的构造函数和类型。

【问题讨论】:

  • 我认为将newtype的构造函数隐藏在内部模块中不是一种选择吗?如果是,您可以将 newtype 构造函数重命名为(例如)Positive_,将 Positive 模式同义词移至内部模块并导出。
  • 我认为这里已经回答了这个问题:stackoverflow.com/questions/8172548/…从那时起没有任何变化。这个 GHC trac 票建议不要创建新的模式同义词,而只是在当前构造函数上使用patternghc.haskell.org/trac/ghc/ticket/8753 但不幸的是,这也会导出构造函数,而不仅仅是模式:(因为默认情况下构造函数模式是双向的。
  • 为什么要模式和哑构造函数同名?为什么不把愚蠢的构造函数命名为别的呢?无论如何,您的用途都看不到它。还是我误会了……
  • 当您尝试在模块中定义与导入模块中的名称相同的名称时,您最好使用import qualified 和限定引用,而不是尝试设置非常挑剔的导入以使隐式不合格的命名空间中包含您想要的内容。
  • @luqui b/c 它使从使用内部 API 到外部 API 的重构变得非常容易。改一下import语句,然后,只要我不使用笨拙的构造函数,我就没事了。

标签: haskell module


【解决方案1】:

如果我错了,请纠正我,但我几乎可以肯定这就是你要找的:

{-# 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 Negative where

import Positive

bar :: Maybe Int
bar = getPositive <$> toPositive 6

让我们再次检查 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

【讨论】:

  • 完美!我不知道您可以使用 pattern 关键字指定构造函数。非常感谢。
猜你喜欢
  • 2020-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-21
  • 2013-02-22
  • 2012-04-21
相关资源
最近更新 更多