为什么Haskell允许使用Shape列表,但没有Square或Circle或Triangle列表

Mic*_*ron 2 oop polymorphism haskell functional-programming list

为什么Haskell允许在第一个例子中做一个Shape列表,但不像第二个例子那样?据我所知,两个列表都有元素

{ name :: String, position :: Vector3D, radius :: Double }
Run Code Online (Sandbox Code Playgroud)

要么

{ name :: String, position :: Vector3D, dimensions :: Vector3D }.

前1:

data Shape
    = Sphere { name :: String, position :: Vector3D, radius :: Double }
    | Prism { name :: String, position :: Vector3D, dimensions :: Vector3D }
Run Code Online (Sandbox Code Playgroud)

前2:

data Sphere = Sphere { name :: String, position :: Vector3D, radius :: Double }
data Prism = Prism { name :: String, position :: Vector3D, dimensions :: Vector3D }
Run Code Online (Sandbox Code Playgroud)

我想知道为什么可以做一个Shape列表,但没有Sphere和Prism的多态列表,即使它们具有与通过数据类型"Shape"声明的成员相同的成员.

And*_*ewC 10

不同种类

在你的第二个例子中,

data Sphere = SphereTag { sphereName :: String, 
                          spherePosition :: Vector3D, 
                          sphereRadius :: Double }
data Prism = PrismTag { prismName :: String, 
                        prismPosition :: Vector3D, 
                        prismDimensions :: Vector3D }
Run Code Online (Sandbox Code Playgroud)

你已经声明这些是两种不同的数据类型.你可以有类型[Sphere][Prism]而不是[Shape](因为你没有定义Shape在这个例子中类型).

我已经改名为田,否则name有两个类型name :: Sphere -> Stringname :: Prism -> String,这是不允许的,而无需使用类型类.

我已重命名SphereTagPrismTag明确了类型Sphere和数据构造函数之间的区别SphereTag

一种类型

在第一个例子中

data Shape
   = SphereShape { name :: String, position :: Vector3D, radius :: Double }
   | PrismShape { name :: String, position :: Vector3D, dimensions :: Vector3D }
Run Code Online (Sandbox Code Playgroud)

一种类型,所以你可以制作[Shape].

要么 - 标记联盟

结合两种类型的经典方法是使用Either,它使用Left或标记两种类型的数据Right:

type PrismOrSphere = Either Prism Sphere

myList = [Left (SphereTag "this" ...), Right (PrismTag "that" ....), ....]
Run Code Online (Sandbox Code Playgroud)

但你的自定义Shape类型可能会更好.

来自OOP的建议

尽量不要将OOP教学示例重新用作函数式编程示例.OOP示例旨在首先教授OO原则,然后编程编程,它们的设计非常适合开发OO和命令式思维.

这就像试图通过在空车停车场学习驾驶飞机一样.在飞机上停车很慢,但这可能是你开车时学到的第一件事.

如果你坚持通过试图复制你的驾驶课程来学习飞行,你会发现你的飞机是一种非常不方便的汽车,不适合很多道路.

您应该使用一套精心编写的示例来教授函数式编程.我推荐Learn You a Haskell for Great Good,它包含web和dead tree版本.

OOP与FP的多态性

在面向对象编程中,您通常称为多态的是使用超类实现的.你可以拥有一个由Shape子类型组成的ShapeList或一个由管理器和清理器组成的EmployeeList,但是在传统的OOP中,你需要编写不同但相似的代码来实现.sort每个方法的方法.我们可以称之为这种亚型多态性.它与从泛型中获得的多态性不同,在泛型中您可以编写单个方法来处理任何类型.

在函数式编程中,你通常称之为多态的是通过完全不知道你的数据类型来实现的,所以你可以编写一个函数reverse :: [a] -> [a],它可以用于任何可能的列表,Shapes或Spheres或Employees或者其他什么,更像是泛型但是没有运行时类型的数据开销(参见Haskell擦除类型吗?).我们可以称之为参数多态.它与从类型类中获得的多态性不同,在类型类中,允许多个类型具有相同的命名函数.


mb1*_*b14 6

因为在EX1,SpherePrism不是类型,但构造函数.它们都属于同一类型Shape.因此,您可以创建一个列表[Shape].而且,因为它们不是类型,[Sphere]所以甚至没有意义,因为Sphere它不在类型命名空间中.我不知道这是否令你感到困惑,但是当我开始学习Haskell时,我混淆了构造函数和OO子类.它们是不同的.在那种情况下(ex1)你不能有这样的事情:

radius :: Sphere -> Double
Run Code Online (Sandbox Code Playgroud)

因为Sphere不是一种类型.Sphere是一个返回的函数Shape.

现在,在EX2,Sphere并且Prism是类型的,所以你要么有

  • 一系列领域 [Sphere]
  • 一系列棱镜 [Prim]
  • 安德鲁克指出的一个或一个订单的清单[Either Sphere Prism].

另一种解决方案是为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
Run Code Online (Sandbox Code Playgroud)

现在,您可以拥有一个列表同质的形状,例如:

names :: Shape s => [s] -> [String]
names ss = map name ss
Run Code Online (Sandbox Code Playgroud)

同质,我的意思是你不能在同一个列表中混合棱镜和球体.