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
.