不同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
Plane
和Sphere
实例。
我试图将球体和飞机存储在同一个列表中,但它不起作用。 我明白,它不应该工作,因为Sphere
和Plane
是两种不同的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不支持向下转换,所以通过消除Plane
和Sphere
之间的表示区别,不会丢失任何信息。 具体的数据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新的Contain
types构造函数来显式添加每个组件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语言进行一些扩展,即ExplicitForAll
和ExistentialQuantification
。 我们必须明确地定义showIt
(使用模式匹配来解构AnyShow
types),因为不能使用字段名称来使用存在量化的数据types。
有更多的解决scheme(希望另一个答案将使用Data.Dynamic
– 如果没有人做,你有兴趣然后阅读它,并随时发布任何问题,阅读产生的)。