I need to implement a chess game for a school assignment, and you have to make an interface that will work for other games on the same board. So, you have to implement chess pieces, but also pieces for other games.
I tried to do this:
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)) }
Then I try to load the begin f the chess game with:
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)
And I get the error "Couldn't match expected type Piece' with actual type
ChessPiece'
In the first argument of f', namely
Rook'
In the first argument of Just', namely
(f Rook)'
In the expression: Just (f Rook)"
So, I've the feeling that the ChessPiece needs to be 'casted' to a (regular) Piece somehow. (I know, I am using terms from imperative programming, but I hope that I make myself clear here, I will be happy to make my question clearer if needed).
Is the construct that I'm trying to make possible? (sort of like a class structure from OO languages, but then applied to datatypes, where one datatype is a sub-datatype from the other, and an object can be two datatypes at the same time. For example a Rook is a ChessPiece and therefore a Piece) What am I doing wrong? Any suggestions on how to implement the structure I need?
The data types can roughly be described as: numbers, booleans, characters, arrays, and structures. Some languages like ActionScript replace characters with "strings". Object oriented languages, such as C++ and Java replace "structures" with "objects".
Yes we can store different/mixed types in a single array by using following two methods: Method 1: using Object array because all types in . net inherit from object type Ex: object[] array=new object[2];array[0]=102;array[1]="csharp";Method 2: Alternatively we can use ArrayList class present in System.
Lists can have multiple object types.
An object can store multiple values, just like an array, but individual values in an object are accessed by a key, not by a numerical index.
What you are after is normally referred to as sub-typing. Most OO languages achieve sub-typing using sub-classes.
Haskell, however, is decidedly not an OO language; in fact, it does not really have any sort of sub-typing at all. Happily, you can usually achieve much the same effect using "parametric polymorphism". Now, "parametric polymorphism" is a scary-sounding term! What does it mean?
In fact, it has a very simple meaning: you can write code that works for all (concrete) types. The Maybe
type, which you already know how to use, is a great example here. The type is defined as follows:
data Maybe a = Just a | Nothing
note how it is written as Maybe a
rather than just Maybe
; the a
is a type variable. This means that, when you go to use Maybe
, you can use it with any type. You can have a Maybe Int
, a Maybe Bool
, a Maybe [Int]
and even a Maybe (Maybe (Maybe (Maybe Double)))
.
You can use this approach to define your board. For basic board functions, you do not care about what "piece" is actually on the board--there are some actions that make sense for any piece. On the other hand, if you do care about the type of the piece, you will be caring about what the type is exactly, because the rules for each game are going to be different.
This means that you can define your board with some type variable for pieces. Right now, your board representation looks like this:
data Board = Board {boardData :: Array Pos (Maybe ColoredPiece)}
since you want to generalize the board to any sort of piece, you need to add a type variable instead of specifying ColoredPiece
:
data Board p = Board {boardData :: Array Pos p}
now you've defined a Board
type for any piece type you could possibly imagine!
So, to use this board representation for chess pieces, you need to pass the type of the piece to your new Board
type. This will look something like this:
type ChessBoard = Board ColoredPiece
(For reference, type
just creates a synonym--now writing ChessBoard
is completely equivalent to writing Board ColoredPiece
.)
So now, whenever you have a chess board, use your new ChessBoard
type.
Additionally, you can write some useful functions that work on any board. For example, let's imagine all you want to do is get a list of the pieces. The type of this function would then be:
listPieces :: Board p -> [p]
You can write a whole bunch of other similar functions that don't care about the actual piece by using type variables like p
in your function types. This function will now work for any board you give it, including a Board ColoredPiece
, otherwise know as ChessBoard
.
In summary: you want to write your Board
representation polymorphically. This lets you achieve the same effect as you wanted to try with sub-typing.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With