多种数据类型的对象

标签 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 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)

我收到错误消息“无法匹配预期类型 Piece' with actual type ChessPiece”
f', namely 的第一个参数中车'
Just', namely 的第一个参数中(f Rook)'
在表达中:Just (f Rook)"

所以,我觉得 ChessPiece 需要以某种方式“类型转换”到(常规)Piece。
(我知道,我使用的是命令式编程中的术语,但我希望我在这里说清楚,如果需要,我很乐意让我的问题更清楚)。

我试图实现的构造是否成为可能? (有点像 OO 语言中的类结构,但随后应用于数据类型,其中一种数据类型是另一种数据类型的子数据类型,并且一个对象可以同时是两种数据类型。例如,Rook 是 ChessPiece,因此一块)
我究竟做错了什么?关于如何实现我需要的结构的任何建议?

最佳答案

您所追求的通常称为子类型。大多数面向对象语言使用子类实现子类型化。

然而,Haskell 绝对不是面向对象的语言。事实上,它根本没有任何类型的子类型。令人高兴的是,您通常可以使用“参数多态性”获得相同的效果。现在,“参数多态性”是一个听起来很可怕的术语!这是什么意思?

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

data Maybe a = Just a | Nothing

注意它是如何写成 Maybe a而不仅仅是 Maybe ; a是一个类型变量。这意味着,当你去使用 Maybe ,您可以将其用于任何类型。您可以拥有一个 Maybe Int , Maybe Bool , Maybe [Int]甚至还有一个 Maybe (Maybe (Maybe (Maybe Double))) .

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

这意味着您可以为棋子定义一些类型变量的棋盘。现在,您的董事会代表如下所示:
data Board = Board {boardData :: Array Pos (Maybe ColoredPiece)}

由于您想将板子推广到任何类型的块,您需要添加一个类型变量而不是指定 ColoredPiece :
data Board p = Board {boardData :: Array Pos p}

现在你已经定义了一个 Board键入您可以想象的任何作品类型!

因此,要将此棋盘表示用于国际象棋棋子,您需要将棋子的类型传递给新的 Board类型。这看起来像这样:
type ChessBoard = Board ColoredPiece

(作为引用, type 只是创建了一个同义词——现在写 ChessBoard 完全等同于写 Board ColoredPiece 。)

所以现在,只要你有棋盘,就用你的新 ChessBoard类型。

此外,您可以编写一些适用于任何电路板的有用函数。例如,让我们假设您想要做的就是获取碎片列表。此函数的类型将是:
listPieces :: Board p -> [p]

您可以使用像 p 这样的类型变量来编写一大堆其他类似的函数,这些函数不关心实际的部分。在您的函数类型中。此功能现在适用于您提供的任何板,包括 Board ColoredPiece , 否则称为 ChessBoard .

总之:你要写你的Board多态表示。这可以让您获得与您想要尝试使用子类型相同的效果。

关于多种数据类型的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12990329/

相关文章:

haskell - 在 Haskell 中隐藏约束

go - col.ToStrings 未定义(类型 Columns 没有字段或方法 ToStrings)

sql - 相当于 BigQuery 标准 SQL 中的 typedef

php - CakePHP 2.0 对象不是数组

c# - 在一个对象中创建一类对象

JavaScript concat 没有按预期工作,需要详细说明吗?

haskell - 能够从 Haskell 中的 monadic action 中获得多态函数

haskell - 检查给定的 Integer 是否是元组列表中元组的第一个元素

java - 如何将不同类型的ArrayList传递给方法?

PHP 尝试在 WooCommerce Api 2.0.1 中获取订单 ID 创建订单功能