It's full of stuff like
flipPlayer : Player -> Player
flipPlayer currentPlayer =
case currentPlayer of
X ->
O
O ->
X
which is 8 lines where idiomatic Haskell would take 3. flipPlayer : Player -> Player
flipPlayer X = O
flipPlayer O = X
I don't know if Elm encourages or mandates that verbose style but it's not some sort of "FP" requirement.