多种数据类型的对象

Rub*_*ben 5 constructor haskell types object

我需要为学校作业实施一个国际象棋游戏,你必须创建一个适用于同一板上其他游戏的界面.所以,你必须实现国际象棋棋子,但也需要其他游戏的棋子.

我试着这样做:

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)) }
Run Code Online (Sandbox Code Playgroud)

然后我尝试加载国际象棋游戏的开头:

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)
Run Code Online (Sandbox Code Playgroud)

我得到错误"无法匹配预期类型Piece' with actual typeChessPiece"在f', namelyRook 的第一个参数中'在第一个参数中Just', namely(f Rook)'在表达式中:Just(f Rook)"

因此,我觉得ChessPiece需要以某种方式"铸造"到(常规)作品.(我知道,我正在使用命令式编程中的术语,但我希望我在这里说清楚,如果需要,我会很乐意让我的问题更清楚).

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

Tik*_*vis 10

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

然而,Haskell绝对不是OO语言; 事实上,它根本没有任何类型的子类型.令人高兴的是,使用"参数多态"通常可以获得相同的效果.现在,"参数多态"是一个可怕的术语!这是什么意思?

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

data Maybe a = Just a | Nothing
Run Code Online (Sandbox Code Playgroud)

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

您可以使用此方法来定义您的电路板.对于基本的板功能,你不关心板上实际上是什么"件" - 有些动作对任何一件都有意义.在另一方面,如果你做的片约的类型照顾,你会关心的类型是什么确切的,因为每场比赛的规则将是不同的.

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

data Board = Board {boardData :: Array Pos (Maybe ColoredPiece)}
Run Code Online (Sandbox Code Playgroud)

因为你想将板子推广到任何类型的片段,你需要添加一个类型变量而不是指定ColoredPiece:

data Board p = Board {boardData :: Array Pos p}
Run Code Online (Sandbox Code Playgroud)

现在你已经Board为你想象的任何一种类型定义了一种类型!

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

type ChessBoard = Board ColoredPiece
Run Code Online (Sandbox Code Playgroud)

(仅供参考,type只需创建一个同义词 - 现在写作ChessBoard完全等同于写作Board ColoredPiece.)

所以现在,每当你有一个国际象棋棋盘时,请使用你的新ChessBoard类型.

此外,您可以编写一些适用于任何电路板的有用功能.例如,让我们想象你要做的就是得到一些碎片清单.那么这个函数的类型是:

listPieces :: Board p -> [p]
Run Code Online (Sandbox Code Playgroud)

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

总结:您希望以Board多态方式编写表示.这使您可以实现与想要使用子类型尝试相同的效果.