diff --git a/2023/9.hs b/2023/9.hs
new file mode 100755
index 0000000..6da8d3a
--- /dev/null
+++ b/2023/9.hs
@@ -0,0 +1,27 @@
+#!/usr/bin/env runhaskell
+
+parseInput :: String -> [[Integer]]
+parseInput = map (map read . words) . lines
+
+solve str = show (part1', part2')
+  where
+    input = parseInput str
+    part1' = part1 input
+    part2' = part2 input
+
+part1 xs = sum $ map next xs
+part2 xs = sum $ map prev xs
+
+deltas xs = zipWith (flip (-)) xs $ tail xs
+differentiate xs = takeWhile notAllZeros $ iterate deltas xs
+  where
+    notAllZeros xs = not $ all (== 0) xs
+
+next xs = sum $ map last $ differentiate xs
+
+prev xs = foldl1 (flip (-)) $ reverse $ map head $ differentiate xs
+
+main :: IO ()
+main = do
+  contents <- getContents
+  putStrLn $ solve contents
diff --git a/2023/README.md b/2023/README.md
index 8059552..d2326a5 100644
--- a/2023/README.md
+++ b/2023/README.md
@@ -1,20 +1,22 @@
 # Advent of Code 2023
 
-|  S  |  M  |  T  |  W  |  T  |  F  |  S  |
-| :-: | :-: | :-: | :-: | :-: | :-: | :-: |
-|     |     |     |     |     | [1] | [2] |
-| [3] | [4] | [5] |  6  | [7] | [8] | [9] |
-| 10  | 11  | 12  | 13  | 14  | 15  | 16  |
-| 17  | 18  | 19  | 20  | 21  | 22  | 23  |
-| 24  | 25  |     |     |     |     |     |
-
+|  S  |  M  |  T  |  W  |  T  |  F  |              S               |
+| :-: | :-: | :-: | :-: | :-: | :-: | :--------------------------: |
+|     |     |     |     |     | [1] |             [2]              |
+| [3] | [4] | [5] |  6  | [7] | [8] | 9 [elixir][9], [haskell][9h] |
+| 10  | 11  | 12  | 13  | 14  | 15  |              16              |
+| 17  | 18  | 19  | 20  | 21  | 22  |              23              |
+| 24  | 25  |     |     |     |     |                              |
 
 [1]: ./lib/2023/1.ex
 [2]: ./lib/2023/2.ex
 [3]: ./lib/2023/3.ex
 [4]: ./lib/2023/4.ex
 [5]: ./lib/2023/5.ex
+
 <!-- [6]: ./lib/2023/6.ex -->
+
 [7]: ./lib/2023/7.ex
 [8]: ./lib/2023/8.ex
 [9]: ./lib/2023/9.ex
+[9h]: ./9.hs