I was reading http://learnyouahaskell.com/a-fistful-of-monads, but felt that the moving multiple steps part could be made more flexible using Monoid, so here’s my attempt. The key is that given a step function, a -> [a], from the current state to multiple next states, how to make it a monoid so that we can merge multiple into one.

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
26
27
28
29
import Control.Monad
import Data.Coerce

type Pos = (Int, Int)

move :: Pos -> [Pos]
move (c,r) = do
(c',r') <- [(c+2,r-1),(c+2,r+1),(c-2,r-1),(c-2,r+1)
,(c+1,r-2),(c+1,r+2),(c-1,r-2),(c-1,r+2)
]
guard (c' `elem` [1..8] && r' `elem` [1..8])
return (c',r')

newtype MoveT = MoveT (Pos -> [Pos])

run :: Int -> Pos -> [Pos]
run n p = moves p
where
moves = coerce . mconcat $ replicate n (coerce move :: MoveT)

instance Semigroup MoveT where
(MoveT x) <> (MoveT y) = MoveT $ x >=> y

instance Monoid MoveT where
mempty = MoveT pure

main = do
print $ run 1 (6, 2)
print $ run 2 (6, 2)