不同types的列表?

data Plane = Plane { point :: Point, normal :: Vector Double } data Sphere = Sphere { center :: Point, radius :: Double } class Shape s where intersect :: s -> Ray -> Maybe Point surfaceNormal :: s -> Point -> Vector Double 

我也做了Shape PlaneSphere实例。

我试图将球体和飞机存储在同一个列表中,但它不起作用。 我明白,它不应该工作,因为SpherePlane是两种不同的types,但他们都是Shape实例,所以不应该工作? 我如何将形状和平面存储在列表中?

 shapes :: (Shape t) => [t] shapes = [ Sphere { center = Point [0, 0, 0], radius = 2.0 }, Plane { point = Point [1, 2, 1], normal = 3 |> [0.5, 0.6, 0.2] } ] 

这个问题代表了面向对象和function思维的转折点。 有时,即使是复杂的哈斯克勒也仍然处在这种心理转变之中,他们的devise常常落入托马斯的答案中提到的存在型types模式。

这个问题的function性解决scheme涉及到将typestypes转换为数据types(通常一旦完成,typestypes的需求就消失了):

 data Shape = Shape { intersect :: Ray -> Maybe Point, surfaceNormal :: Point -> Vector Double } 

现在你可以很容易地构造一个Shape的列表,因为它是一个单形的types。 由于Haskell不支持向下转换,所以通过消除PlaneSphere之间的表示区别,不会丢失任何信息。 具体的数据types变成构造Shape的函数:

 plane :: Point -> Vector Double -> Shape sphere :: Point -> Double -> Shape 

如果您无法捕捉到您需要了解Shape数据types中所有Shape信息,您可以使用代数数据types来枚举这些案例,正如Thomas所build议的那样。 但如果可能的话,我会build议不要这样做; 而是尝试find所需形状的基本特征,而不是仅仅列举示例。

你正在寻找一个异类列表,大多数Haskellers并不特别喜欢,即使他们在第一次学习Haskell时也问过自己同样的问题。

你写:

 shapes :: (Shape t) => [t] 

这就是说这个列表有typest ,所有这些都是相同的,并且恰好是一个Shape(形状相同!)。 换句话说 – 不,它不应该如何工作。

两种常见的方式来处理它(首先是一个Haskell 98方法,然后是我不推荐的另一种更奇妙的方法):

使用一个新的types静态联合感兴趣的子types:

 data Foo = F deriving Show data Bar = B deriving Show data Contain = CFoo Foo | CBar Bar deriving Show stuffExplicit :: [Contain] stuffExplicit = [CFoo F, CBar B] main = print stuffExplicit 

这很好看,因为它很简单,你不会失去有关列表中包含的信息。 你可以确定第一个元素是一个Foo ,第二个元素是一个Bar 。 您可能已经意识到,缺点是您必须通过创build新的Containtypes构造函数来显式添加每个组件types。 如果这是不可取的,继续阅读。

使用存在types :另一种解决scheme包括丢失关于元素的信息 – 您只需保留元素在特定类中的知识即可。 因此,您只能使用列表元素上的该类的操作。 例如,下面只记得Show类的元素,所以你可以对元素做的唯一事情就是在Show使用多态的函数:

 data AnyShow = forall s. Show s => AS s showIt (AS s) = show s stuffAnyShow :: [AnyShow] stuffAnyShow = [AS F, AS B] main = print (map showIt stuffAnyShow) 

这需要对Haskell语言进行一些扩展,即ExplicitForAllExistentialQuantification 。 我们必须明确地定义showIt (使用模式匹配来解构AnyShowtypes),因为不能使用字段名称来使用存在量化的数据types。

有更多的解决scheme(希望另一个答案将使用Data.Dynamic – 如果没有人做,你有兴趣然后阅读它,并随时发布任何问题,阅读产生的)。