Chapter 16: Functor¶
https://hackage.haskell.org/package/base-4.14.1.0/docs/Data-Functor.html https://wiki.haskell.org/Typeclassopedia#Functor
16.1 Functor¶
Rudolf Carnap, a logician in the 1930s, coined the term functor to describe grammatical function words that operate over sentences or phrases.
Understanding Functor
and Applicative
is important
to a deep understanding of Monad
.
This chapter will include:
the return of the higher-kinded types;
fmaps galore, and not only on lists;
no more digressions about dusty logicians;
words about type classes and constructor classes;
puns based on George Clinton music, probably.
16.2 What’s a functor?¶
A functor is a way to apply a function to the inhabitants of some structure that we don’t want to alter.
-- 16.2 What's a functor, page 628, fig 1
--
--
-- The input
-- type constructor
-- must take one type
-- argument to become
-- a concrete type.
-- vvvvvvvvvvvvv
class Functor (f :: * -> *) where
--
--
-- This is the
-- functorial
-- type that
-- contains a.
-- (By taking May be a differnt
-- it as a type than a.
-- type arg.) /
-- v v
fmap :: (a -> b) -> f a -> f b
-- ^^^^^^^^ ^
-- Function to |
-- perform on the This is the same f!
-- enclosed type.
--
--
-- ⍘(<$)⍘ Has a default definition of ⍘fmap . const⍘.
(<$) :: a -> f b -> f a
--
--
-- We only need fmap for a complete definition of
-- this type class (though we should be nice and
-- follow the Functor laws).
{-# MINIMAL fmap #-}
16.4 Let’s talk about f baby¶
We know that the f
in our Functor
definition must
be kind * -> *
for a couple of reasons, which we will
first describe and then demonstrate:
Each argument (and result) in the type signature must be fully applied. Each argument must have the kind
*
.The type
f
was applied to a single argument in two different places,f a
andf b
. Sincef a
andf b
must each have the kind*
,f
by itself must be kind* -> *
.
16.4.1 Shining star come into view¶
Every argument to the type constructor of ->
must be of kind *
:
·∾ :kind (->)
(->) :: * -> * -> *
In other words; Each argument and result of every function must be a type constant (or concrete type), rather than a type constructor awaiting arguments.
Given this knowledge, we can know something about
Functor
from the type of fmap
:
class Functor f where
fmap :: (a -> b) -> f a -> f b
-- * * *
Remember that type variables are scoped to the type class
declaration that introduced them. The f
in class
Functor (f :: * -> *) where { ... }
is the same f
within the type signature of everything withing braces.
16.4.5 A shining star for you to see what your f can truly be¶
Notice a similarity between $
and <$>
(infix
fmap
):
(<$>) :: Functor f => (a -> b) -> f a -> f b
$ :: (a -> b) -> a -> b
16.17 Chapter Exercises¶
16.17.1 Valid Functor¶
Determine if a valid functor can be written for the datatype provided.
data Bool = False | True
Ok, this type doesn’t contain anything, and the entire point of functor is to map over the contents of some structure (or context), so intuitively it doesn’t make sense for functor to work for this type.
Accordingly, at the implementation level, the kind of
Bool
isBool :: *
, which doesn’t meetFunctor
’s kind constraint of(f :: * -> *)
.So we can’t write a lawful instance for this type. But, for all I know, maybe GHC can’t really enforce the kind signature. I know it can’t enforce laws. What will happen if I try?
This:
·∾ :{ ⋮ instance Functor Bool where ⋮ fmap f l = not l ⋮ :} <interactive>:3:18: error: • Expected kind ‘* -> *’, but ‘Bool’ has kind ‘*’ • In the first argument of ‘Functor’, namely ‘Bool’ In the instance declaration for ‘Functor Bool’
Good. Sane error messages are nice :^).
data BoolAndSomethingElse a = False' a | True' a
Alright! This type constructor takes an argument, and has a structure, so it makes sense to write a functor for it. The fact that the data constructors have the words true and false in them is completely irrelevant – they may as well be named
A
andB
.Let me give this a shot:
·∾ data BoolAndSomethingElse a = False' a | True' a deriving (Eq, Show) ·∾ :{ ⋮ instance Functor BoolAndSomethingElse where ⋮ fmap f (False' a) = False' (f a) ⋮ fmap f (True' a) = True' (f a) ⋮ :} ·∾ ·∾ fmap (*3) (True' 20) True' 60 ·∾ fmap (++"?") (False' "what") False' "what?"
data BoolAndMaybeSomethingElse a = Falsish | Truish a
Alright, this one looks similar to
Maybe
:·∾ data BoolAndMaybeSomethingElse a = Falsish | Truish a deriving (Eq, Show) ·∾ :{ ⋮ instance Functor BoolAndMaybeSomethingElse where ⋮ fmap f (Truish a) = Truish (f a) ⋮ fmap f Falsish = Falsish ⋮ :} ·∾ ·∾ fmap (++ "in a bottle") (Truish "a message") Truish "a messagein a bottle"
Use the kinds to guide you on this one, don’t get too hung up on the details.
newtype Mu f = InF { outF :: f (Mu f) }
This… doesn’t seem right…:
·∾ newtype Mu f = InF { outF :: f (Mu f) } ·∾ :kind Mu Mu :: (* -> *) -> * ·∾ :kind Functor Functor :: (* -> *) -> Constraint
I don’t think it’s possible to define an instance for this datatype, but I’ll revisit the problem after I do some more reading in the chapter.
Again, follow the kinds and ignore the unfamiliar parts
import GHC.Arr data D = D (Array Word Word) Int Int
The kind signature doesn’t match, so I don’t think I can define an instance for it; I’ll revisit this after reading more, though.
·∾ import GHC.Arr ·∾ data D = D (Array Word Word) Int Int deriving (Eq, Show) ·∾ :kind D D :: * ·∾ :kind Functor Functor :: (* -> *) -> Constraint
16.17.2 Constructor shuffle¶
Rearrange the arguments to the type constructor of the datatype so the Functor instance works.
data Sum a b = First a | Second b instance Functor (Sum e) where fmap f (First a) = First (f a) fmap f (Second b) = Second b
It compiles!
data Sum b a = First a | Second b deriving (Eq, Show) instance Functor (Sum e) where fmap f (First a) = First (f a) fmap f (Second b) = Second b
data Company a b c = DeepBlue a c | Something b instance Functor (Company e e') where fmap f (Something b) = Something (f b) fmap _ (DeepBlue a c) = DeepBlue a c
I don’t know why this works, but it does.
data Company a c b = DeepBlue a c | Something b deriving (Eq, Show) instance Functor (Company e e') where fmap f (Something b) = Something (f b) fmap _ (DeepBlue a c) = DeepBlue a c
data More a b = L a b a | R b a b deriving (Eq, Show) instance Functor (More x) where fmap f (L a b a') = L (f a) b (f a') fmap f (R b a b') = R b (f a) b'
Keeping in mind that it should result in a
Functor
that does the following:·∾ fmap (+1) (L 1 2 3) L 2 2 4 ·∾ fmap (+1) (R 1 2 3) R 1 3 3
data More b a = L a b a | R b a b deriving (Eq, Show) instance Functor (More x) where fmap f (L a b a') = L (f a) b (f a') fmap f (R b a b') = R b (f a) b'
16.17.3 Write the instance¶
Write Functor
instances for the following datatype.
data Quant a b = Finance | Desk a | Bloor b
Alright, here is the first instance:
data Quant a b = Finance | Desk a | Bloor b deriving (Eq, Show) instance Functor (Quant a) where fmap f Finance = Finance fmap f (Desk a) = Desk a fmap f (Bloor b) = Bloor (f b) -- I don't know what the I'm doing, here. So frustrating! instance Arbitrary (Quant Int Int) where arbitrary = elements ( [ Finance ] ++ take 10 [ (Desk a) | a <- [1..] ] ++ take 10 [ (Bloor a) | a <- [1..] ] ) --
No, it’s not interesting by itself.
data K a b = K a
I still learned something:
--data K a b = K a ---- justsomeguy ---- ---- Is it impossible to define an instance of Functor ---- for the type ''data K a b = K a''? ---- ---- I keep on getting type errors if I try something ---- like ''instance Functor (K a) where { fmap f (K ---- a) = K (f a) }''. ---- ---- koz_ ---- ---- justsomeguy: That's just Const. So it is very ---- possible. ---- ---- The problem you're having is that 'f' has a ---- different type to what you're thinking. It's ---- not a -> c, it's _b_ -> c. ---- ---- In Const, the second type parameter is phantom. ---- So you can change it to whatever whenever. ---- ---- So you just ignore the function and rebuild the ---- Const with the same value 'inside' it always had. ---- ---- You can think of 'Const a b' as 'an a pretending ---- to be a b'. So we can have it pretend to be a ---- c, and you only pass the function so fmap is happy. ---- ---- So the question becomes, why does f take a "b" as ---- input rather than an "a"? ---- ---- It turns out that instance declarations operate on the ---- outermost (or rightmost) type argument first. This is ---- due to the outermost reduction strategy that Haskell ---- employes. ---- ---- More here: ---- ---- https://www.youtube.com/watch?v=QBQ9_9R7o8I ---- &list=PLe7Ei6viL6jGp1Rfu0dil1JH1SHk9bgDV ---- &index=31 ---- ---- One property of the church-rosser theorem is: ---- ---- If there is some reduction strategy that terminates, ---- the outermost reduction strategy will *always* terminate. ---- --instance Functor (K a) where -- fmap f (K a) = K a ---- (K a) b ---- fmap ---- Prelude> :t K ---- K :: a -> K a b ---- Prelude> :kind K ---- K :: * -> * -> * --
newtype Flip f a b = Flip (f b a) deriving (Eq, Show) newtype K a b = K a -- should remind you of an instance you've written before instance Functor (Flip K a) where fmap = undefined
data EvilGoateeConst a b = GoatyConst b -- You thought you'd escaped the goats by now didn't you? Nope.
No, it doesn’t do anything interesting. No magic here or in the previous exercises. If it works, you succeeded.
Do you need something extra to make the instance work?
data LiftItOut f a = LiftItOut (f a)
data Parrapa f g a = DaWrappa (f a) (g a)
Don’t ask for more typeclass instances than you need. You can let GHC tell you what to do.
data IgnoreOne f g a b = IgnoringSomething (f a) (g b)
data Notorious g o a t = Notorious (g o) (g a) (g t)
You’ll need to use recursion.
data List a = Nil | Cons a (List a)
A tree of goats forms a Goat-Lord, fearsome poly-creature.
data GoatLord a = NoGoat | OneGoat a | MoreGoats (GoatLord a) (GoatLord a) (GoatLord a) -- A VERITABLE HYDRA OF GOATS
You’ll use an extra functor for this one, although your solution might do it monomorphically without using
fmap
. Keep in mind that you will probably not be able to validate this one in the usual manner. Do your best to make it work.data TalkToMe a = Halt | Print String a | Read (String -> a)