Eric Bailey

Written on 2 December, 2023
Updated on 02 January, 2024

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))