Data.Functor.Day
Data.Functor.Day is an interesting type. It takes two functors and two values, and is a functor itself. Here is its definition.
data Day f g a = forall b c. Day (f b) (g c) (b -> c -> a)
For example, you can create values like these.
day1 = Day [1, 2, 3] (Just 10) (+)
day2 = Day [1, 2, 3] Nothing (+)
day3 = Day (Identity True) ("abc", 'b') (,)
The type of day1 and day2 is Num a => Day [] Maybe a, and the type of day3 is Day Maybe ((,) String) (Bool, Char).
What can you do when you have day1? Can you extract [1, 2, 3] or 10 from it? No, you can’t. It’s because b and c are existential types. For example, you cannot write a function like this.
extract1 :: Num a => Day [] Maybe a -> [a]
extract1 (Day fb gc bca) = fb
Also, you cannot write a function like this.
extract2 :: Day Identity ((,) String) (Bool, Char) -> Bool
extract2 (Day (Identity b) gc bca) = b
As you can see, even though you can always extract b from Identity b, you cannot extract it from Day.
Then what can you do? The only thing you can do is applying bca :: b -> c -> a to b and c. For example, you can apply bca to each element in the list with c in the Maybe if it’s Just c.
doSomething :: Num a => Day [] Maybe a -> [a]
doSomething (Day fb (Just c) bca) = map (flip bca c) fb
doSomething (Day _ _ _) = []
doSomething day1 will be [11, 12, 13], and doSomething day2 will be [].
Note that you can, of course, extract a value once you’ve applied bca. For example, you can write this extract2' instead. This is because the bca in day3 just puts two values into a pair. It completely makes sense to get the first element from it.
extract2' :: Day Identity ((,) String) (Bool, Char) -> Bool
extract2' (Day (Identity b) (_, c) bca) = fst (bca b c)
To sum up, you can say that you can control how you’ll apply bca based on functors f and g, but you cannot extract f a, g b, a nor b directly. This character of Day becomes important when you think about natural isomorphism between Day Identity f and f where f is Functor.