HasPlained: Functor & Applicative
For the "begintermediate" Funk(y-nota)tional Programmer, until more auto-math-ic intuition sets in, a quick-lookup cheat-sheet: examples of the built-in common standard instances for these two foundational Haskell typeclasses (for when GHCi isn't at hand, which it should never be).
Functor — <$> , <$
A Functor
, to "get mapped over", declares fmap :: (a→b) → (f a) → (f b)
aka. <$>
(this flipped being >~
in my code-bases) with laws:
-id <$> foo
≡ id foo
((f <$>).(g <$>)) foo
≡f.g <$> foo
baz <$ foo
≡(const baz) <$> foo
Applicative — pure , <> , < , *>
An Applicative
(also always a Functor
) "declares a functor with extra laws and operations to enable more/structured functorial
computations". Meaning approximately,
pure
brings a valuea
into the functorf
asf a
, and<*>
allows applying some function-in-the-functorf (a->b)
(often a partial application obtained from an earlier<$>
) to such a value-in-the-functorf a
to obtain the returned such value-in-the-functorf b
.<**>
it not aflip
of<*>
but of some deeper inner$
, ie. "convoluted ×2"*>
discards left-hand side result,<*
same for right-hand but only because:(<* func_in_functor)
is not aflip
of*>
but of the original function encapsulated byfunc_in_functor
!
All a tad too knotty for my taste, but the basics still need to be understood for comprehending Haskell code-bases. As for the
laws.. similarly labyrinthine,
mostly needed when writing instances (such as on top of a custom Functor
) or for reasoning about existing Applicative-style code.
Maybe
Given dis = Nothing
and dat = Just "foo"
Functor — <$> , <$
dis>~reverse
≡reverse<$> dis
≡fmap reverse dis
≡Nothing
dat>~reverse
≡reverse<$> dat
≡fmap reverse dat
≡Just "oof"
"baz" <$ dis
≡Nothing
"baz" <$ dat
≡Just "baz"
Applicative — pure , <> , < , *>
Further also given cmp = compare <$> dem
where dem = (pure "bar") :: Maybe String
≡ dem = Just "bar"
cmp <*> dis
≡ (here)dis <**> cmp
≡Nothing
cmp <*> dat
≡ (here)dat <**> cmp
≡Just LT
cmp *> dis
≡dis <* cmp
≡Nothing
cmp *> dat
≡dat <* cmp
≡Just "foo"
Just 10 <* Just "20"
≡Just 10
Just 10 *> Just "20"
≡Just "20"
Just _ *> Nothing
≡Nothing *> Just _
≡Just _ <* Nothing
≡Nothing <* Just _
≡Nothing
Either t
Given dis = Left "baz"
and dat = Right "foo"
Functor — <$> , <$
dis>~reverse
≡reverse<$> dis
≡fmap reverse dis
≡Left "baz"
dat>~reverse
≡reverse<$> dat
≡fmap reverse dat
≡Right "oof"
"bar" <$ dis
≡Left "baz"
"bar" <$ dat
≡Right "bar"
Applicative — pure , <> , < , *>
Further also given cmp = compare <$> dem
where dem = (pure "bar") :: Either String String
≡ dem = Right "bar"
cmp <*> dis
≡ (here)dis <**> cmp
≡Left "baz"
cmp <*> dat
≡ (here)dat <**> cmp
≡Right LT
cmp *> dis
≡dis <* cmp
≡Left "baz"
cmp *> dat
≡dat <* cmp
≡Right "foo"
Right 10 <* Right "20"
≡Right 10
Right 10 *> Right "20"
≡Right "20"
Left anything *> _
≡_ *> Left anything
≡Left anything <* _
≡_ <* Left anything
≡Left anything
(t,)
Given disndat = ("baz","foo")
Functor — <$> , <$
disndat>~reverse
≡reverse<$> disndat
≡fmap reverse disndat
≡("baz","oof")
"bar" <$ disndat
≡("baz","bar")
Applicative — pure , <> , < , *>
Further also given cmp = compare <$> dem
where dem = (pure "bar") :: (String,String)
≡ dem = ("","bar")
cmp <*> disndat
≡ (here)disndat <**> cmp
≡("baz",LT)
cmp *> disndat
≡disndat <* cmp
≡("baz","foo")
("a",'b') <* ("c",'d')
≡("ac",'b')
("a",'b') *> ("c",'d')
≡("ac",'d')
(GT,True) <* (LT,False)
≡(GT,True)
(GT,True) *> (LT,False)
≡(GT,False)
[]
Given dis = []
and dat = ["foo","baz"]
Functor — <$> , <$
dis>~reverse
≡reverse<$> dis
≡fmap reverse dis
≡[]
dat>~reverse
≡reverse<$> dat
≡fmap reverse dat
≡["oof","zab"]
"bar" <$ dis
≡[]
"bar" <$ dat
≡["bar","bar"]
Applicative — pure , <> , < , *>
Further also given cmp = compare <$> dem
where dem = (pure "dem") :: [String]
≡ dem = ["dem"]
cmp <*> dis
≡ (here)dis <**> cmp
≡[]
cmp <*> dat
≡ (here)dat <**> cmp
≡[LT,GT]
cmp *> dis
≡dis <* cmp
≡[]
cmp *> dat
≡dat <* cmp
≡["foo","baz"]
[1,2] <* [3,4]
≡[1,1,2,2]
[1,2] <* [3,4,5]
≡[1,1,1,2,2,2]
[1,2] <* [3,4,5,6]
≡[1,1,1,1,2,2,2,2]
[1,2,3,4] *> [5,6]
≡[5,6,5,6,5,6,5,6]
"yo" *> "foo"
≡"foofoo"
"yo" <* "fee"
≡"yyyooo"
"zoom" *> "ya"
≡"yayayaya"
"zoom" <* "ya"
≡"zzoooomm"
[] *> _
≡_ *> []
≡[] <* _
≡_ <* []
≡[]
[reverse , id] <*> ["foo","bar"]
≡["oof","rab","foo","bar"]
IO
Given dat = (pure "foo") :: IO String
Functor — <$> , <$
dat>~reverse
≡ reverse<$> dat
≡ fmap reverse dat
≡ dat >>= (pure . reverse)
≡ (pure "oof") :: IO String
"baz" <$ dat
≡ (pure "baz") :: IO String
Applicative — pure , <> , < , *>
Further also given dis = (pure "") :: IO String
and cmp = compare <$> dem
where dem = (pure "dem") :: IO String
cmp <*> dis
≡ (here)dis <**> cmp
≡(pure GT) :: IO Ordering
cmp <*> dat
≡ (here)dat <**> cmp
≡(pure LT) :: IO Ordering
cmp *> dis
≡dis <* cmp
≡(pure "") :: IO String
cmp *> dat
≡dat <* cmp
≡(pure "foo") :: IO String
dis *> dat
≡dat <* dis
≡(pure "foo") :: IO String
dat *> dis
≡dis <* dat
≡(pure "") :: IO String
t ->
Given dat = "foo-bar"
and dis = [1,2,3,4]
or []
or [True]
or any other list..
Functor — <$> , <$
(tail>~reverse) dat
≡(reverse <$> tail) dat
≡(fmap reverse tail) dat
≡(reverse . tail) dat
≡(tail~.reverse) dat
≡"rab-oo"
(reverse>~tail) dat
≡(tail <$> reverse) dat
≡(fmap tail reverse) dat
≡(tail . reverse) dat
≡(reverse~.tail) dat
≡"ab-oof"
(reverse>~head) dat
≡(head <$> reverse) dat
≡(fmap head reverse) dat
≡(head . reverse) dat
≡(reverse~.head) dat
≡'r'
(tail <$ head) dis dat
≡tail dat
≡"oo-bar"
(head <$ tail) dis dat
≡head dat
≡'f'
Applicative — pure , <> , < , *>
Really not ready to go there..