List 为什么Haskell允许列出形状,但不允许列出正方形、圆形或三角形
为什么Haskell允许像第一个例子那样列出形状,而不像第二个例子那样?据我所知,两个列表中的元素都是List 为什么Haskell允许列出形状,但不允许列出正方形、圆形或三角形,list,oop,haskell,functional-programming,polymorphism,List,Oop,Haskell,Functional Programming,Polymorphism,为什么Haskell允许像第一个例子那样列出形状,而不像第二个例子那样?据我所知,两个列表中的元素都是 { name :: String, position :: Vector3D, radius :: Double } 或 {name::String,position::Vector3D,dimensions::Vector3D} 例1: 例2: 我想知道为什么可以创建一个形状列表,但不能创建Sphere和Prism的多态列表,即使它们的成员与通过数据类型“Shape”声明的成员相同。您所说
{ name :: String, position :: Vector3D, radius :: Double }
或
{name::String,position::Vector3D,dimensions::Vector3D}
例1:
例2:
我想知道为什么可以创建一个形状列表,但不能创建Sphere和Prism的多态列表,即使它们的成员与通过数据类型“Shape”声明的成员相同。您所说的实际上并不正确,您提供的两个示例都不是列表 上面的示例是Algebric数据类型的示例 您显示的第二个示例是Sphere的记录数据声明示例:
data Sphere = Sphere { name :: String, position :: Vector3D, radius :: Double }
data Prism = Prism { name :: String, position :: Vector3D, dimensions :: Vector3D }
也就是说,在Haskell列表中是同质类型。它们只能保存相同类型的值。(尽管您可以通过使用存在量化来克服这一问题,但不要将其作为反模式使用。)
同样从您的示例一中,您可以将其作为类型[Shape]
的列表,该列表可以包含球体
和棱镜
。例如:
λ> let a = Sphere "sph1" 2.2 3.4
λ> let b = Prism "prism" 3.4 4.8
λ> let c = [a,b]
λ> :t c
c :: [Shape]
在上面的例子中,我假设Vector3D
属于Double
不同的类型
在你的第二个例子中
data Sphere = SphereTag { sphereName :: String,
spherePosition :: Vector3D,
sphereRadius :: Double }
data Prism = PrismTag { prismName :: String,
prismPosition :: Vector3D,
prismDimensions :: Vector3D }
您已经声明这是两种不同的数据类型。您可以使用类型[Sphere]
和[Prism]
,但不能使用[Shape]
(因为在本例中您尚未定义形状
类型)
我之所以重命名这些字段,是因为除此之外name
有两种类型name::Sphere->String
和name::Prism->String
,如果不使用typeclass,这是不允许的
我已经重命名了SphereTag
和PrismTag
,以明确类型Sphere
和数据构造函数SphereTag
一种
在第一个例子中
data Shape
= SphereShape { name :: String, position :: Vector3D, radius :: Double }
| PrismShape { name :: String, position :: Vector3D, dimensions :: Vector3D }
有一种类型,因此您可以制作[Shape]
任意标记的并集
组合两种类型的经典方法是使用其中一种,即使用Left
或Right
标记两种类型的数据:
type PrismOrSphere = Either Prism Sphere
myList = [Left (SphereTag "this" ...), Right (PrismTag "that" ....), ....]
但是你最好还是使用你的自定义形状类型
来自OOP的建议
尽量不要将OOP教学示例作为函数编程示例重复使用。OOP示例的目的是先教您OO原则,然后再教您编程,它们的设计非常适合于开发您的OO和命令式思维
这就像是在一个空旷的停车场附近驾驶飞机一样。在飞机上绕着停车场慢慢走是很困难的,而这可能是你开车时学到的第一件事
如果你坚持通过模仿驾驶课程来学习飞行,你只会认为你的飞机是一种非常不方便的汽车,不能在很多道路上行驶
您应该使用一组编写良好的示例来教授函数式编程。我推荐web版本和死树版本
面向对象与FP中的多态性
在面向对象编程中,通常所称的多态性是使用超类实现的。您可以使用由Shape子类型组成的ShapeList,也可以使用由经理和清洁工组成的EmployeeList,但在传统的OOP中,您需要编写不同但相似的代码来对每种类型实现。sort
方法。我们可以称这种亚型为多态性。它不同于从泛型中获得的多态性,在泛型中,您可以编写一个方法来处理任何类型
在函数式编程中,通常所称的多态性是通过完全不知道数据类型来实现的,因此您可以编写一个函数
反向::[a]->[a]
,该函数适用于任何可能的列表、形状、球体或雇员等,更像泛型,但没有运行时类型的数据开销(请参阅)。我们可以称之为参数多态性。这与类型类中的多态性不同,在类型类中,您允许多个类型具有相同的命名函数。我想您可能会对Haskell中类型的工作方式感到困惑。Haskell使用参数多态性和基于类的即席多态性。它没有任何类似于结构子类型的东西。为数据构造函数的字段指定的名称与其中声明的类型绑定;它们不能在其他地方重复使用。即使有允许这种重用的GHC扩展,重用的名称也彼此不相关。因为在ex1中,Sphere
和Prism
不是类型,而是构造函数。它们都是相同类型的形状
。因此,您可以创建[Shape]
的列表。此外,由于它们不是类型,[Sphere]
甚至没有意义,因为Sphere
不在类型命名空间中。我不知道这是否让你感到困惑,但当我开始学习Haskell时,我把构造函数和OO子类混为一谈。他们是不同的。在这种情况下(ex1),您不能有以下情况:
radius :: Sphere -> Double
因为Sphere
不是一种类型Sphere
是一个返回Shape
的函数
现在,在ex2中,Sphere
和Prism
是类型,因此您可以选择其中一种
- 球体列表
[Sphere]
- 棱镜列表
[Prim]
- AndrewC
指出的一个或多个顺序的列表[任一球面棱镜]
Shape
类
class Shape a where
name :: a -> String
position :: a -> Vector3D
data Sphere = Sphere { sphereName :: String, spherePosition :: Vector3D, ... }
instance Shape Sphere where
name s = sphereName s
position s = spherePosition
data Prism = Prism { primsName :: String, prismPosition :: Vector3D, ... ?
instance Shape Prirm where
name p = prismName p
position p = prismPosition p
现在,您可以有一个同质形状列表,例如:
names :: Shape s => [s] -> [String]
names ss = map name ss
“同质”是指不能在同一个列表中混合使用棱柱体和球体。如果我理解正确,听起来您的理解是数据类型
data Shape
= Sphere { name :: String, position :: Vector3D, radius :: Double }
| Prism { name :: String, position :: Vector3D, dimensions :: Vector3D }
和t
class Shape a where
name :: a -> String
position :: a -> Vector3D
data Sphere = Sphere { sphereName :: String, spherePosition :: Vector3D, ... }
instance Shape Sphere where
name s = sphereName s
position s = spherePosition
data Prism = Prism { primsName :: String, prismPosition :: Vector3D, ... ?
instance Shape Prirm where
name p = prismName p
position p = prismPosition p
names :: Shape s => [s] -> [String]
names ss = map name ss
data Shape
= Sphere { name :: String, position :: Vector3D, radius :: Double }
| Prism { name :: String, position :: Vector3D, dimensions :: Vector3D }
data Sphere = Sphere { name :: String, position :: Vector3D, radius :: Double }
data Prism = Prism { name :: String, position :: Vector3D, dimensions :: Vector3D }