Let's say I write the following code:
game module
module Game where import Player import Card data Game = Game {p1 :: Player, p2 :: Player, isP1sTurn :: Bool turnsLeft :: Int }
player module
module Player where import Card data Player = Player {score :: Int, hand :: [Card], deck :: [Card] }
and card module
module Card where data Card = Card {name :: String, scoreValue :: Int}
Then I write code to implement the logic, when players take turns drawing and playing cards from their own hand to add bonuses to their account until the game is over.
However, after completing this code, I realized that the written game module is boring!
I want to reorganize the card game, so when you play on the card, and not just add an account, the card will arbitrarily transform the game instead.
So, I am changing the Card module to the next
module Card where import Game data Card = Card {name :: String, onPlayFunction :: (Game -> Game) scoreValue :: Int}
which, of course, makes importing modules a loop form.
How to solve this problem?
Trivial solution:
Move all files to the same module. This solves the problem well, but reduces modularity; I cannot reuse the same map module for another game.
Module Maintenance Solution:
Add a type parameter to Card :
module Card where data Card a = {name :: String, onPlayFunc :: (a -> a), scoreValue :: Int}
Add another parameter to Player :
module Player where data Player a {score :: Int, hand :: [card a], deck :: [card a]}
With one final modification of the Game :
module Game where data Game = Game {p1 :: Player Game, p2 :: Player Game, }
This is modular, but requires me to add parameters to my data types. If the data structures were more deeply nested, I would have to add a lot of parameters to my data, and if I had to use this method for several solutions, I could get a cumbersome number of type modifiers.
So, are there any other useful solutions to solve this refactor, or are these just two options?