Let's say I'm programming a chess game. At some point I have to check, which moves are valid for a given piece. What would be the proper way to select the correct pathfinding function for a given piece (king, queen, etc.)?
I came up with three different approaches:
- Store the pathfinding method (alongside the other data) inside a record (similar to the strategy pattern; too object oriented?)
- Create a
PieceType
Discriminated Union with all piece types and store a value of this type in the record. Select the proper pathfinding function via pattern matching - Create a
PieceData
record-type which stores the data and aPiece
DU, where all cases represent different piece types and are of typePieceData
. Pattern matching is also used to select the correct function.
Here is some sample code, demonstrating the different approaches:
// The piece "constructor" is private as to only allow creating pieces via
// the provided construct functions for each piece type
type Piece = private { Position:Position; Pathfinder:Position->Board->Position list }
let kingPathfinder position board =
// Check and return all possible targets for a king
let createKing position =
{ Position=position; Pathfinder=kingPathfinder }
let getPossibleTargets { Position=position; Pathfinder=pathfinder } board =
pathfinder position board
#### #### ####
type PieceType =
| King
| ...
type Piece = { Position:Position; PieceType:PieceType }
let kingPathfinder position board =
// Check and return all possible targets for a king
let createKing position =
{ Position=position; PieceType=King }
let getPossibleTargets { Position=position; PieceType=pieceType } board =
match pieceType with
| King -> kingPathfinder position board
| ...
#### #### ####
type PieceData = { Position:Position }
type Piece =
| King of PieceData
| ...
let kingPathfinder position board =
// Check and return all possible targets for a king
let createKing position =
King { Position=position }
let getPossibleTargets piece board =
match piece with
| King { Position:position } ->
kingPathfinder position board
My question now is: Which one of these three approaches is preferred in functional programming? Or is there another, superior way?