This is an old revision of the document!
Lab 9. Algebraic Data Types
9.0. Some theory
Types are and essential component in haskell. They start with capital letters, like Int
. We have multiple ways of customizing data types.
The first one is type aliases, signaled by the keyword type
. These allow us to rename complex types to simpler types. They are used for better clarity.
type Matrix = [[a]] type StudentName = String
The second one is data
. This enables us to create new data types. These types can take parameters. Using |
allows us to define a sum type (a type with multiple constructors).
Take a look at Maybe a
(this is a type that already exists in haskell). We can do a lot of things with these, including pattern matching.
data BinaryDigit = Zero | One data Maybe a = Just a | Nothing data List a = Cons a (List a) | Void data Both a b = Both a b
A particular case of data is newtype
. This is an optimization for single-parameter single-constructor types. Without going into details, this is used to better expose the contained type.
We can think of data constructors like pointers (that's how they are implemented). Newtype replaces the pointer with the uderlying value, leading to less memory access and increased performance.
newtype WithIndex a = WithIndex (a, Int)
For practical usecases, we might need a way to model more complex data. In other languages, this can be achieved using structs
, classes
, interfaces
etc. Haskell allows us to model data
using records
. A record is a specific type of data very similar to a struct. We use deriving Show
as a way to be able to convert the type to string when printing it in the terminal.
data Dog = Dog{ name :: String , breed :: String , age :: String } deriving Show
Now, we can create a Dog like this.
myDog = Dog {name="Spike", breed="Golden Retriever", age = 5}
How can we access the dog's information? Simple. Haskell implements getters for us by default
name myDog --"Spike" breed myDog --"Golder Retriever" age myDog --5
After the lab, you can take a look at some more cool stuff related to data types in haskell and functional types in general
Unboxed types
Lenses
9.1. Chess Basics
9.1.1 Let's implement A ChessResult
data type. In chess, a game result can be either a Win, a Draw or a Loss. Make sure to add deriving Show
.
data ChessResult = ??? deriving Show
9.1.2 In chess, your performance is rated based on a point system. A win is equivalent to a point, a draw is 0.5 points and a loss is 0 points
points :: ChessResult -> Float points = ???
9.1.3 We want to find out the number of point a player earns after playing multiple games. Try to implement this as a closure.
score :: [ChessResult] -> Float
9.1.4 Now that we can measure a player's score, we want to establish a ranking. Return the descending list of scores. Hint: sort
ranking :: [[ChessResult]] -> [Float] ranking = ???
9.1.5 Each tournament also offers a cash prize, based on player performance. Compute the earnings for each player. The formula we'll use is player_earnings = player points * prize money / total points
earnings :: [[ChessResult]] -> Float ->[Float] earings = ???
9.2. Chess Tournament
9.2.0 We want to model a chess player using records. We care about 3 things: name, elo(a floating point value which measures a player's strength) and a list of games played (name it tournamentGames). We also want to model a chess tournament. We'll represent it as a binary tree.
data player = Player { ??? } data Tree a = ??? deriving Show type Tournament = Tree Player
9.2.1 After a player plays a game, we want to update his games with the new result.
addResult :: ChessResult -> Player -> Player
9.2.2 It's finally time to play some chess. After the game is played, we want to return 2 players with updated results. The first one should be the winner (will be helpful later). However, everyone knows chess is a luck-based game. As such, a player will win a game if their odds of winning (calculated based on elo difference) is greater than 0.5.
-- odds that A beats B based on their elo winningOdds :: Float -> Float -> Float winningOdds eloA eloB = 1 / (1 + 10** ((eloB-eloA)/400)) playGame :: Player -> Player -> (Player, Player) playGame player1 player2 = ???
9.2.3 A guy with a lot of free time decided it would be fun to play games against multiple opponents. Return the updated player and the updated list of opponents (take a look at mapAccumL
).
multipleMatches :: Player -> [Player] -> (Player, [Player]) multipleMatches = ???
9.2.4 It seems that the chess community is boiling and players are ready to throw hands. Each player must face all other players in a single match. At the end, all players will have their results updated.
groupStage :: [Player] -> [Player] groupStage = ???
9.2.5 Playing every player is a bit boring. We want to organize a tournament to decide the best player faster. We can assume each tournament will have 2^n players for simplicity. Each round, groups of 2 players will face each other, with the winner advancing to the next round. Don't forget to update their results.
tournamentStage :: [Player] -> Tournament tournamentStage = ???
9.2.6 A guy named Carlos Magnusen wants to organize a chess tournament. As he only wants the best players to participate, he decided to organize a big group stage with all players facing each other a tournament stage with the best 2^n groups players (n must be as high as possible).
-- this makes it so you can use sort on a list of players. They'll be ranked based on total points. We'll go into more detail next time. instance Eq Player where p1 == p2 = score (tournamentGames p1) == score (tournamentGames p2) instance Ord Player where p1 < p2 = score (tournamentGames p1) < score (tournamentGames p2) p1 > p2 = score (tournamentGames p1) > score (tournamentGames p2) p1 <= p2 = score (tournamentGames p1) <= score (tournamentGames p2)
chessTournament :: [Player] -> Tournament chessTournament = ???
9.2.7 Carolina wants to see how her beloved Romeo did in his first tournament. However, his rowdy 'chess' friends may have taken him to the local chess bar instead, so he may have not played at all. Implement the function findPlayerByName
, to find a player's result in a tournament(1 if he is the winner, 2 if he reached semifinals, 3 for quarterfinals etc)
findPlayerByName :: String -> Tournament -> Maybe Int findPlayerByName = ???