Object 多数据类型的对象

Object 多数据类型的对象,object,haskell,types,constructor,Object,Haskell,Types,Constructor,我需要为学校作业实现一个国际象棋游戏,并且您必须制作一个界面,该界面将适用于同一板上的其他游戏。因此,您必须实现国际象棋棋子,但也要实现其他游戏的棋子 我试着这样做: data ChessPiece = King | Queen | Knight | Rook | Bishop | Pawn deriving (Enum, Eq, Show) data Piece = ChessPiece | OtherGamePiece deriving (Enum, Eq, Show) data Color

我需要为学校作业实现一个国际象棋游戏,并且您必须制作一个界面,该界面将适用于同一板上的其他游戏。因此,您必须实现国际象棋棋子,但也要实现其他游戏的棋子

我试着这样做:

data ChessPiece = King | Queen | Knight | Rook | Bishop | Pawn deriving (Enum, Eq, Show)
data Piece = ChessPiece | OtherGamePiece deriving (Enum, Eq, Show)
data ColoredPiece = White Piece | Black Piece
data Board = Board { boardData :: (Array Pos (Maybe ColoredPiece)) }
然后,我尝试加载国际象棋游戏的开始:

beginBoard = Board (listArray (Pos 0 0, Pos 7 7) (pieces White ++ pawns White ++ space ++ pawns Black ++ pieces Black)) where
    pieces :: (Piece -> ColoredPiece) -> [Maybe ColoredPiece]
    pieces f = [Just (f Rook), Just (f Knight), Just (f Bishop), Just (f Queen), Just (f King), Just (f Bishop), Just (f Knight), Just (f Rook)]
    pawns :: (Piece -> ColoredPiece) -> [Maybe ColoredPiece]
    pawns f = (take 8 (repeat (Just (f Pawn))))
    space = take 32 (repeat Nothing)
我得到的错误是“无法将预期类型<代码>棋子'与实际类型<代码>棋子'匹配” 在
f'的第一个参数中,即
Rook' 在
Just'的第一个参数中,即
(f Rook)' 在表达式中:Just(f Rook)”

因此,我觉得棋子需要以某种方式“铸造”成(常规)棋子。 (我知道,我使用的是命令式编程中的术语,但我希望我在这里讲清楚,如果需要的话,我会很乐意把我的问题讲清楚)

我想让这个结构成为可能吗?(有点像OO语言中的类结构,但随后应用于数据类型,其中一个数据类型是另一个的子数据类型,一个对象可以同时是两个数据类型。例如,Rook是一个棋子,因此是一个棋子)
我做错了什么?关于如何实现我需要的结构有什么建议吗?

您所追求的通常称为子类型。大多数OO语言使用子类实现子类型

然而,Haskell显然不是一种OO语言;事实上,它根本没有任何类型的子类型。幸运的是,您通常可以使用“参数多态性”实现大致相同的效果。现在,“参数多态性”是一个听起来很可怕的术语!这是什么意思

事实上,它有一个非常简单的含义:您可以编写适用于所有(具体)类型的代码。
Maybe
类型就是一个很好的例子,您已经知道如何使用它了。该类型定义如下:

data Maybe a = Just a | Nothing
注意它是如何写为
可能是一个
,而不仅仅是
可能
a
是一个类型变量。这意味着,当您使用
Maybe
时,您可以将其与任何类型一起使用。你可以有一个
可能是Int
,一个
可能是Bool
,一个
可能是[Int]
甚至一个
可能(可能(可能(可能是双重))

您可以使用此方法定义您的电路板。对于基本的电路板功能,您不关心电路板上实际有什么“部件”——有些操作对任何部件都有意义。另一方面,如果你真的关心棋子的类型,你就会关心棋子的确切类型,因为每个游戏的规则都是不同的

这意味着您可以使用某些类型变量来定义电路板。现在,您的董事会代表如下所示:

data Board = Board {boardData :: Array Pos (Maybe ColoredPiece)}
type ChessBoard = Board ColoredPiece
由于要将电路板概括为任何类型的电路板,因此需要添加类型变量,而不是指定
ColoredPiece

data Board p = Board {boardData :: Array Pos p}
现在,您已经为您可能想象的任何工件类型定义了
类型

因此,要将此棋盘表示法用于棋子,您需要将棋子类型传递给新的
棋盘
类型。这将看起来像这样:

data Board = Board {boardData :: Array Pos (Maybe ColoredPiece)}
type ChessBoard = Board ColoredPiece
(作为参考,
type
只是创建了一个同义词——现在写
ChessBoard
完全等同于写
Board-ColoredPiece

所以现在,每当你有一个棋盘,使用你新的
棋盘
类型

此外,您还可以编写一些在任何板上工作的有用函数。例如,让我们想象一下,您所要做的就是获得一个片段列表。该函数的类型为:

listPieces :: Board p -> [p]
通过在函数类型中使用
p
之类的类型变量,您可以编写一大堆其他类似的函数,而不关心实际的部分。此功能现在适用于您提供的任何棋盘,包括
棋盘彩色棋子
,否则称为
棋盘


总之:您希望以多态方式编写
表示。这可以让你达到与你想要尝试的子类型相同的效果。

Tikhon的解决方案是一条可行之路。不过,请注意类型构造函数和数据构造函数之间的区别。就在这里,例如:

data ChessPiece = King | Queen | Knight | Rook | Bishop | Pawn deriving (Enum, Eq, Show)
data Piece = ChessPiece | OtherGamePiece deriving (Enum, Eq, Show)
这是行不通的,因为您在第一行定义了一个名为
ChessPiece
的类型构造函数,在另一行定义了一个名为
ChessPiece
的数据构造函数,而这两个构造函数是不同的。类型构造函数会这样说:“一个
ChessPiece
类型可以是
King
,或者
Queen
,或者……。”而数据构造函数只是创建一般数据(这也恰好被称为
ChessPiece

您可以做的是为
片段
类型重新定义第一个数据构造函数;一些被称为
ChessPiece
的通用数据,在引擎盖下携带有关类型
ChessPiece
的一些信息。以下类型检查:

data ChessPiece   = King | Queen | Knight | Rook | Bishop | Pawn deriving (Enum, Eq, Show)
data Piece        = ChessPiece ChessPiece | OtherGamePiece  -- note the change 
data ColoredPiece = White Piece | Black Piece
你可以这样改变你的功能:

pieces :: (Piece -> ColoredPiece) -> [Maybe ColoredPiece]
pieces f = [Just (f (ChessPiece Rook)), Just (f (ChessPiece Knight)), Just (f (ChessPiece Bishop)), Just (f (ChessPiece Queen)), Just (f (ChessPiece King)), Just (f (ChessPiece Bishop)), Just (f (ChessPiece Knight)), Just (f (ChessPiece Rook))]
为了使类型构造函数和数据构造函数之间的区别更加明显,这里有一个有限的版本,它为每个构造函数使用不同的名称:

data ChessRoyalty = King | Queen
data Piece        = ChessPiece ChessRoyalty | OtherGamePiece
data ColoredPiece = White Piece | Black Piece

谢谢,这真的帮了我的忙!我在读《真实世界哈斯克尔》,这让我的大脑严重融化:)。谢谢你对其他像我一样正在学习哈斯克尔的凡人的精彩回复。啊,这就是我一直在寻找的!非常感谢,我不知道我自己怎么没有想到这一点!