为了掌握更好的类型类(从几乎开始形式开始)我开始使用面积计算来建模二维形状,如下所示:
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
如何在外面可见模块。我不确定我在期待什么,但不是这个。
我真正喜欢的是类型类,它的方法和“智能构造函数”在模块外可见而没有别的。可以这样做吗?
答案 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
的类型。