类型和模块如何交互?

时间:2010-07-12 11:05:46

标签: haskell module visibility typeclass

为了掌握更好的类型类(从几乎开始形式开始)我开始使用面积计算来建模二维形状,如下所示:

module TwoDShapes where

class TwoDShape s where
    area :: s -> Float

data Circle = Circle Float deriving Show
aCircle radius | radius < 0 = error "circle radius must be non-negative"
               | otherwise  = Circle radius
instance TwoDShape Circle where
    area (Circle radius) = pi * radius * radius

data Ellipse = Ellipse Float Float deriving Show
anEllipse axis_a axis_b | axis_a < 0 || axis_b < 0 = error "ellipse axis length must be non-negative"
                        | otherwise                = Ellipse axis_a axis_b
instance TwoDShape Ellipse where         
    area (Ellipse axis_a axis_b) = pi * axis_a * axis_b

等等其他形状。

这很好,但是我想到了这个:

module TwoDShapes  where

class TwoDShape s where
    area :: s -> Float

data TwoDShapeParams = TwoDShapeParams Float Float Float deriving Show

instance TwoDShape TwoDShapeParams where
    area (TwoDShapeParams length_a length_b constant) = foldl (*) 1 [length_a, length_b, constant]

aCircle radius | radius < 0 = error "circle radius must be non-negative"
               | otherwise  = TwoDShapeParams radius radius pi

anEllipse axis_a axis_b | axis_a < 0 || axis_b < 0 = error "ellipse axis length must be non-negative"
                        | otherwise                = TwoDShapeParams axis_a axis_b pi

等。这也没关系。以信息隐藏为目标,我将模块声明更改为:

module TwoDShapes (TwoDShape, area, aCircle, anEllipse, aRectangle, aTriangle)

稍微让我惊讶的是1)工作和2)在ghci aCircle 评估为TwoDShapeParams 1.0 1.0 3.1415927这是真的,但我不明白类型TwoDShapeParams如何在外面可见模块。我不确定我在期待什么,但不是这个。

我真正喜欢的是类型类,它的方法和“智能构造函数”在模块外可见而没有别的。可以这样做吗?

2 个答案:

答案 0 :(得分:6)

虽然隐藏了TwoDShapes的表示,但您已为其派生了Show个实例,该实例允许将TwoDShapes类型的任意值转换为String ,这就是信息泄露的根源。真正抽象的类型不应该定义Show实例,或者实际上是Data实例,它同样公开有关表示的信息。只要String与表示无关,就可以将您的类型转换为String,这是Show和{Data.Map.Map Data.Array.Array 1}}这是很好的例子。)

请注意,模块系统正在完成其工作:您仍然无法引用定义它的模块之外的TwoDShapes构造函数。

答案 1 :(得分:1)

如果您在ghci提示符中看到*TwoDShapes,它可以访问模块中的所有内容:

  

http://www.haskell.org/ghc/docs/6.12.1/html/users_guide/interactive-evaluation.html

     

新提示是* Main,表示我们在Main模块的顶层上下文中键入表达式。我们刚加载的模块Main中顶级范围内的所有内容都在提示符的范围内(可能包括Prelude,只要Main没有明确隐藏它)。

     

语法*模块指示它是模块的完整顶级范围,它对提示符处键入的表达式的范围做出贡献。没有*,只显示模块的导出。

尝试在没有TwoDShapes的情况下加载*并检查aCircle的类型。