2010-07-12 81 views
4

为了把握更好的类型类(首发几乎从零开始的形式),我在模型2-d的形状一展身手与面积计算,像这样:类型类和模块如何交互?

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如何在模块外部可见。我不确定我期待的是什么,但不是这个。

我真的很喜欢typeclass,它的方法和“聪明的构造函数”是可见的模块外,没有别的。可以这样做吗?

回答

6

虽然TwoDShapes表示是隐藏的,你已经推导出它Show实例,它允许TwoDShapes类型的任意值被转换成一个String,所以这是信息泄漏源。一个真正抽象的类型不应该定义一个Show实例,或者实际上同样暴露有关该表示的信息的实例。只要String与表示无关(请参阅Data.Map.MapData.Array.ArrayShow实例以了解此示例),就可以将其转换为String

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

+0

我感到惊讶(我没有在这台计算机上安装Haskell编译器,所以无法检查会发生什么)是,'aCircle'可以在所有非导出类型'TwoDShapeParams' (不是它的值构造函数)出现在它的类型中。 – 2010-07-12 12:06:26

+0

您编写了'Module TwoDShapes(TwoDShape,...',它输出* type *'TwoDShape',但不是它的表示。但是,即使你没有导出类型,你仍然可以导出一个类型为“TwoDShape”的函数,否则你就无法在其他地方引用'TwoDShape'。 Haskell模块系统控制标识符的可见性,这就是它的全部功能。 – 2010-07-12 13:17:10

+0

我不是问题的作者:) – 2010-07-12 13:37:38

1

如果在ghci的提示看*TwoDShapes,它可以在模块中获得的一切:

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

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

语法*模块表明它是模块的完整顶级范围,它有助于提示处键入的表达式的范围。如果没有*,只能看到模块的输出。

尝试加载TwoDShapes没有*和检查aCircle的类型是什么。