为了掌握更好的类型类(几乎从零开始),我尝试使用面积计算对二维形状进行建模,如下所示:
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
在模块外可见。我不确定我在期待什么,但不是这个。我真正想要的是类型类、它的方法和“智能构造函数”在模块外部可见,没有别的。可以这样做吗?
最佳答案
虽然 TwoDShapes
的表示已隐藏,您已导出 Show
例如,它允许 TwoDShapes
类型的任意值转换为 String
,所以这就是信息泄露的来源。真正的抽象类型不应定义 Show
例如,或者实际上是 Data
类似地暴露有关表示的信息的实例。有一种方法可以将您的类型转换为 String
,只要 String
是独立于表示的(参见 Show
和 Data.Map.Map
的 Data.Array.Array
实例以获取很好的示例)。
请注意,模块系统正在发挥作用:您仍然不能引用 TwoDShapes
定义它的模块之外的构造函数。
关于haskell - 类型类和模块如何交互?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3227891/