Monadic Vectors
Eric Bailey
Written on 2 December, 2023
Updated on 02 January, 2024
Tags: haskell, monads, mathematics, algebra
The Functor
instance for 3-dimensional vectors applies a function f
to each
basis vector, preserving the structure of the 3-D vector.
instance Functor V3 where fmap f (V3 a b c) = V3 (f a) (f b) (f c) a <$ _ = V3 a a a
λ> fmap (+1) (V3 1 2 3) V3 2 3 4
The Applicative
instance provides operations to embed pure expressions
(pure
), and sequence computations and combine their results (<*>
and
liftA2
).
N.B. The default definition is:
liftA2 f x y = f <$> x <*> y
instance Applicative V3 where pure a = V3 a a a V3 a b c <*> V3 d e f = V3 (a d) (b e) (c f)
λ> pure 0 :: V3 Int V3 0 0 0 λ> V3 (+5) (+3) (+1) <*> V3 1 2 3 V3 6 5 4
Together they enable applying a binary function as follows.
λ> (+) <$> V3 1 2 3 <*> V3 4 5 6 V3 5 7 9
There's also a Monad
instance, which enables concise and elegant code.
instance Monad V3 where V3 a b c >>= f = V3 a' b' c' where V3 a' _ _ = f a V3 _ b' _ = f b V3 _ _ c' = f c
For example, as part of the Advent of Code puzzle for Day 2 of 2023, one must
parse revelations of the form N COLOR
where N
is a natural
number and
COLOR
is one of red
, green
, and blue
. The tricolor nature of the
revelations (and the subsequent computations therewith) lends itself nicely to
3-dimensional vectors.
A naive Parser
might look as follows.
revelation :: Parser (V3 Integer) revelation = do n <- natural V3 n 0 0 <$ string "red" <|> V3 0 n 0 <$ string "green" <|> V3 0 0 n <$ string "blue"
The Monad
instance, however, enables the following.
revelation :: Parser (V3 Integer) revelation = natural >>= \n -> for (V3 "red" "green" "blue") $ \color -> n <$ string color <|> pure 0
Also delightfully concise is this way of determining which games are possible.
isPossible :: [V3 Integer] -> Bool isPossible = all (and . liftA2 (>=) (V3 12 13 14))