Introduction to Yoneda and Coyoneda

Posted on Jul 4, 2024 DRAFT

url: “https://gist.github.com/gregberns/ede18190d5117eea6fb51815e2eab9b2"

#source/article

The Yoneda lemma can speed up fmap with long lists or deep trees.

-- Defining the type
newtype Yoneda f a = Yoneda {
	runYoneda :: forall x. (a -> x) -> F x
}

instance Functor (Yoneda f) where
	-- fmap :: (a -> b) -> (Yoneda f a -> Yoneda f b)
	fmap f y = Yoneda $ \b2x -> runYoneda y (b2x . f)

We get a function wrapped in Yoneda — delayed execution.

Fusing functions to run with b2x . f — we build computation and run everything, on a list for example, in a single pass.

From Yoneda one-to-one relationship, we get two functions

toYoneda :: f a -> Yoneda f a
toYoneda fa = Yoneda (\ax -> fmap ax fa)

fromYoneda :: Yoneda f a -> f a
fromYoneda y = runYoneda y id

Using Yoneda:

createYo :: Yoneda Maybe Int
createYo = toYoneda $ Just 42

foo :: Maybe Int
foo = fromYoneda $ fmap (*2) $ fmap (+1) createYo
-- Yoneda fuses these two fmaps.

Yoneda can fuse if lists or trees are large.