Created
May 17, 2023 08:25
-
-
Save cboudereau/ff891382a345ca81e8f4c6c836d9837b to your computer and use it in GitHub Desktop.
Tennis kata in fsharp with KISS principle + make illegal state unrepresentable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type Point = Love | Fifteen | Thirty | Forty | |
module Point = | |
//TIP / Closure of operation : Can the score go backward ? | |
let next = function Love -> Fifteen | Fifteen -> Thirty | Thirty | Forty -> Forty | |
type Player = Left | Right | |
(* TIP / Primitive obsession | |
- Negative score | |
- 20, 99, 100 score ? *) | |
type Score = | |
| Points of (Point * Point) | |
| FifteenAll | |
| ThirtyAll | |
| Deuce | |
| Advantage of Player | |
| Game of Player | |
module Score = | |
let start = Points (Love, Love) | |
// TIP / Make illegal states unrepresentable | |
module Player = | |
// TIP / Concrete type transformation : make transition clear by using union types. | |
let win player score = | |
match player, score with | |
| _, Game p -> Game p | |
| Left, Advantage Left | Left, Points (Forty, _) -> Game Left | |
| Right, Advantage Right | Right, Points (_, Forty) -> Game Right | |
| Left, Points (Love, Fifteen) | Right, Points(Fifteen, Love) -> FifteenAll | |
| Left, FifteenAll -> Points (Thirty, Fifteen) | |
| Right, FifteenAll -> Points (Fifteen, Thirty) | |
| Left, Points (Fifteen, Thirty) | Right, Points (Thirty, Fifteen) -> ThirtyAll | |
| Left, ThirtyAll -> Points (Forty, Thirty) | |
| Right, ThirtyAll -> Points (Thirty, Forty) | |
| Left, Points(Thirty, Forty) | Right, Points(Forty, Thirty) -> Deuce | |
| p, Deuce -> Advantage p | |
| Left, Advantage Right | Right, Advantage Left -> Deuce | |
| Left, Points (l, r) -> Points (Point.next l, r) | |
| Right, Points (l, r) -> Points (l, Point.next r) | |
// REPL unit tests //////////////////////////////////////////////////////////////////////// | |
// TIP / REPL style : How about bringing the power of TDD / Unit testing in the REPL ? | |
let shouldEqual actual expected = | |
if actual = expected then actual | |
else failwithf "expected:\n%A\ngot\n%A" expected actual | |
// Left player always wins | |
Score.start | |
|> Player.win Left |> shouldEqual (Points(Fifteen, Love)) | |
|> Player.win Left |> shouldEqual (Points(Thirty, Love)) | |
|> Player.win Left |> shouldEqual (Points(Forty, Love)) | |
|> Player.win Left |> shouldEqual (Game Left) | |
// Left wins the game but the right player is close to win | |
Score.start | |
|> Player.win Left |> shouldEqual (Points(Fifteen, Love)) | |
|> Player.win Right |> shouldEqual FifteenAll | |
|> Player.win Left |> shouldEqual (Points (Thirty, Fifteen)) | |
|> Player.win Right |> shouldEqual ThirtyAll | |
|> Player.win Left |> shouldEqual (Points(Forty, Thirty)) | |
|> Player.win Right |> shouldEqual Deuce | |
|> Player.win Left |> shouldEqual (Advantage Left) | |
|> Player.win Right |> shouldEqual Deuce | |
|> Player.win Right |> shouldEqual (Advantage Right) | |
|> Player.win Left |> shouldEqual Deuce | |
|> Player.win Left |> shouldEqual (Advantage Left) | |
|> Player.win Left |> shouldEqual (Game Left) | |
// Now exact the same game but with Right as a winner :) | |
Score.start | |
|> Player.win Right |> shouldEqual (Points(Love, Fifteen)) | |
|> Player.win Left |> shouldEqual FifteenAll | |
|> Player.win Right |> shouldEqual (Points (Fifteen, Thirty)) | |
|> Player.win Left |> shouldEqual ThirtyAll | |
|> Player.win Right |> shouldEqual (Points(Thirty, Forty)) | |
|> Player.win Left |> shouldEqual Deuce | |
|> Player.win Right |> shouldEqual (Advantage Right) | |
|> Player.win Left |> shouldEqual Deuce | |
|> Player.win Left |> shouldEqual (Advantage Left) | |
|> Player.win Right |> shouldEqual Deuce | |
|> Player.win Right |> shouldEqual (Advantage Right) | |
|> Player.win Right |> shouldEqual (Game Right) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment