Function Applicative for Great Good of Leap Year Function

pjschwarz 61 views 25 slides Aug 14, 2024
Slide 1
Slide 1 of 25
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25

About This Presentation

This deck is about the leap_year function shown in https://x.com/Iceland_jack/status/1802659835642528217, i.e.

leap_year :: Integral a => a -> Bool
leap_year = liftA2 (>) (gcd 80) (gcd 50)

Given an integer representing a year, the function returns a boolean indicating if that year is a l...


Slide Content

FunctionApplicativefor Great Good
of Leap Year Function
Polyglot FPfor Fun and Profit –Haskelland Scala
K
Starling
�??????���??????��
Kestrel
B
S
PhoenixΦ
leap_year :: Integral a => a -> Bool
leap_year = liftA2 (>) (gcd 80) (gcd 50)
liftA2 f g = S (B f g)
liftA2 = Φ
liftA2 f g h = S (S (K f) g) h
@philip_schwarzslides by https://fpilluminated.com/

Thisdeckisabouttheleap_yearfunctionshowninthetweetbelow.ItisbeingdefinedinaHaskellREPL.
Givenanintegerrepresentingayear,thefunctionreturnsabooleanindicatingifthatyearisaleapyear.
@philip_schwarz
https://x.com/Iceland_jack/status/1802659835642528217

Theleap_yearfunctionusesbuilt-infunctions(>)andgcd
-- Greater Than function
> :type (>)
(>) :: Ord a => a -> a -> Bool
>(>)2 3
False
> (>)3 2
True
-- Greatest Common Divisor function
> :type gcd
gcd:: Integrala => a -> a -> a
> gcd10 15
5
> gcd10 16
2
> gcd10 17
1
> gcd10 18
2
> gcd10 19
1
> gcd10 20
10
leap_year = liftA2 (>) (gcd 80) (gcd 50)

> leap_year = liftA2 (>) (gcd 80) (gcd 50)
> :type leap_year
leap_year :: Integral a => a -> Bool
> leap_year 2024
True
> leap_year 2025
False
> fmap leap_year [1600, 1700, 1800, 1900, 2000, 2100, 2200, 2300, 2400]
[True,False,False,False,True,False,False,False,True]
leap_yearalsousesliftA2,whichisafunctionprovidedbyApplicative.
> import Control.Applicative
> :type liftA2
liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
Let’sdefineleap_yearandtakeitforaquickspin.
leap_year = liftA2 (>) (gcd 80) (gcd 50)

OperatorDefinition
¬�����
�∨�����
�∧������
�→�??????���ℎ���
�⟺��??????������??????�??????��
Thefollowingslideuseslogicoperators¬,∧,∨,→and⟺.
Hereisareminderoftheirdefinition.

Itlookslikeleap_yearisexploitingthealgorithm
describedinthefollowingtwitter/xthread.
leap_year = liftA2 (>) (gcd 80) (gcd 50)
https://x.com/chordbug/status/1497912619784720384/photo/1
https://x.com/chordbug/status/1497912619784720384

Bytheway,incaseyouareaskingyourselfhowtheprevioussliderefactoredthis
�??????��??????�������⟺4|�∧¬100�∧¬400�))
tothis
�??????��??????�������⟺4|�∧(100�→400�)
seebelowforhowIexplainedittomyself.
IfweapplyDeMorgan’slaw,i.e.
¬�∧�=¬�∨¬�
to
¬100�∧¬400�))
weget
�??????��??????�������⟺4�∧¬(100�∨400|�))
Next,ifweapply¬�∨�=�→�
to
¬100�)∨400�)
weget
�??????��??????�������⟺4|�∧(100�→400�)
whichreadsasfollows:
�is a leap year if and only if both of the following are true
•it is divisible by 4
•if it is divisible by 100, then it is also divisible by 400
refactor
refactor

Whyisitthat,givenfunctiondefinition
leap_year = liftA2 (>) (gcd 80) (gcd 50)
andgivensomeinputyear
e.g.2024
evaluatingleap_year year,amountstoevaluating
(gcd 80 year) > (gcd 50 year)
e.g.
(gcd 802024) > (gcd 502024)
?

HereisthedefinitionofApplicativefunctionliftA2
https://hackage.haskell.org/package/base-4.20.0.1/docs/Prelude.html#liftA2

Butgiventhatthesignatureof liftA2is
liftA2 :: (a -> b -> c) -> f a -> f b -> f c c
howdoes
liftA2 (>) (gcd 80) (gcd 50) 2024
mapto
(gcd 80 2024) > (gcd 50 2024)
?
Thefirststepthatwearegoingtotaketoanswerthisquestion,istoconsidertheactualparametersofliftA2in
liftA2 (>) (gcd 80) (gcd 50) 2024
Thefirstone,i.e.(>),isafunctionwithtypeInt->Int->Bool.
Thesecondone,i.e.(gcd 80)istheresultofapplyingafunctionoftypeInt->Int->Intto80,whichresultsina
functionInt->Int.
Thethirdone,i.e.(gcd 50)istheresultofapplyingafunctionoftypeInt->Int->Intto50,whichalsoresultsina
functionInt->Int.
Giventhatthetypeofleap_yearisInt -> Bool,itfollowsthatin
liftA2 (>) (gcd 80) (gcd 50) 2024
thesignatureofliftA2is
liftA2 :: (Int -> Int -> Bool) -> (Int -> Int) -> (Int -> Int) -> (Int -> Bool)
leap_year = liftA2 (>) (gcd 80) (gcd 50)

Butwhatabstractiondoesfneedtobeinorderfor
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
tobecome
liftA2 :: (Int -> Int -> Boolean) -> (Int -> Int) -> (Int -> Int) -> (Int -> Boolean)
?
Letusdefineftobeafunctionoftyper -> ?,whererissomespecifictype,and?issomeyettobespecifiedtype.
Nowlet’supdatethesignatureofliftA2toreflecttheabovedefinitionoff:
liftA2 :: (a -> b -> c) -> (r -> ?1) -> (r -> ?2) -> (r -> ?3)
Inthecaseathand,i.e.
liftA2 (>) (gcd 80) (gcd 50) 2024
wealreadyknowthat
1.(a -> b -> c)is(Int -> Int -> Bool), i.e. the type of (>),
2.(r -> ?3) is (Int -> Boolean), i.e. the type of leap_year
3.(r -> ?1) and (r -> ?2) are (Int -> Int), i.e. the type of both (gcd 80) and (gcd 50)
soweseethatwithr=Int,?1=Int,?2=Intand?3=Bool,thesignatureofliftA2isindeedthesoughtone:
liftA2 :: (Int -> Int -> Bool) -> (Int -> Int) -> (Int -> Int) -> (Int -> Bool)
leap_year = liftA2 (>) (gcd 80) (gcd 50)

So,toarriveattheliftA2signaturethatisapplicablein
liftA2 (>) (gcd 80) (gcd 50) 2024
i.e.
liftA2 :: (Int -> Int -> Bool) -> (Int -> Int) -> (Int -> Int) -> (Int -> Bool)
wefirsttaketheminimaldefinitionofFunctor
class Functor f where
fmap :: (a -> b) -> f a -> f b
and a minimal definition of Applicative, but with liftA2added to it
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
WethentaketheFunctionFunctorandFunctionApplicative,i.e.theFunctorandApplicativeinstancesfor((->) r),
inwhichfisdefinedtobeafunctionfromsomespecifictypertosomeyetunspecifiedtype.Herearethefunction
signaturesoftheresultinginstances:
fmap :: (a -> b) -> (r -> a) -> (r -> b)
pure :: a -> (r -> a)
(<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b)
liftA2 :: (a -> b -> c) -> (r -> a) -> (r -> b) -> (r -> c)
If we define r = Int, a = Int, b = Intand c = Bool, then liftA2 takes on the desired signature:
liftA2 :: (Int -> Int -> Bool) -> (Int -> Int) -> (Int -> Int) -> (Int -> Bool)

Nowlet’sgetbacktothequestionthatwearelookingtoanswer.
Whyisitthatgivenfunctiondefinition
leap_year = liftA2 (>) (gcd 80) (gcd 50)
andgivensomeinputyear,evaluatingleap_year year,amountstoevaluating
(gcd 80 year) > (gcd 50 year)
?
Orinotherwords,sinceleap_yearisdefinedintermsofliftA2,whyisitthat
liftA2 (>) (gcd 80) (gcd 50) year
evaluatesto
(gcd 80 year) > (gcd 50 year)
?

ItturnsoutthattheliftA2functionoftheFunctionApplicativeisacombinatory
logicfunction(acombinator)calledthephoenix.
Toanswerthequestionrestatedinthepreviousslide,insteadoflookingatthecode
forliftA2,inthenextslidewearegoingtoexploitthefactthatliftA2=phoenix.
liftA2 :: (a -> b -> c) -> (r -> a) -> (r -> b) -> (r -> c)
Φ &#3627408485; &#3627408486; &#3627408487; &#3627408484;=&#3627408485;(&#3627408486; &#3627408484;)(&#3627408487; &#3627408484;)
Φ &#3627408467; &#3627408468; ℎ &#3627408485;=&#3627408467;(&#3627408468; &#3627408485;)(ℎ &#3627408485;)
&#3627408451;ℎ&#3627408476;&#3627408466;&#3627408475;??????&#3627408485;
https://hackage.haskell.org/package/data-aviary-0.4.0/docs/Data-Aviary-Birds.html
rename variables for additional clarity
Φ=λ&#3627408467;.λ&#3627408468;.λℎ.λ&#3627408485;.&#3627408467;&#3627408468;&#3627408485;ℎ&#3627408485;
make ‘point free’

Φ=λ&#3627408467;.λ&#3627408468;.λℎ.λ&#3627408485;.&#3627408467;&#3627408468;&#3627408485;ℎ&#3627408485;
Equation Action
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=????????????&#3627408467;&#3627408481;&#3627408436;2>&#3627408468;&#3627408464;&#3627408465; 80&#3627408468;&#3627408464;&#3627408465; 50 ????????????&#3627408467;&#3627408481;&#3627408436;2=Φ
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=Φ>&#3627408468;&#3627408464;&#3627408465; 80&#3627408468;&#3627408464;&#3627408465; 50 Φ=λ&#3627408467;.λ&#3627408468;.λℎ.λ&#3627408485;.&#3627408467;&#3627408468;&#3627408485;ℎ&#3627408485;
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=λ&#3627408467;.λ&#3627408468;.λℎ.λ&#3627408485;.&#3627408467;&#3627408468;&#3627408485;ℎ&#3627408485;>&#3627408468;&#3627408464;&#3627408465; 80&#3627408468;&#3627408464;&#3627408465; 50 &#3627408467;=>
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=(λ&#3627408468;.λℎ.λ&#3627408485;.>&#3627408468;&#3627408485;ℎ&#3627408485;)&#3627408468;&#3627408464;&#3627408465; 80&#3627408468;&#3627408464;&#3627408465; 50 &#3627408468;=&#3627408468;&#3627408464;&#3627408465; 80
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=(λℎ.λ&#3627408485;.>&#3627408468;&#3627408464;&#3627408465;80&#3627408485;ℎ&#3627408485;)&#3627408468;&#3627408464;&#3627408465; 50 ℎ=&#3627408468;&#3627408464;&#3627408465; 50
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=λ&#3627408485;.>&#3627408468;&#3627408464;&#3627408465;80&#3627408485;&#3627408468;&#3627408464;&#3627408465;50&#3627408485; >&#3627408485;&#3627408486;=&#3627408485;>&#3627408486;
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=λ&#3627408485;.&#3627408468;&#3627408464;&#3627408465;80&#3627408485;>&#3627408468;&#3627408464;&#3627408465;50&#3627408485; apply ??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;to2024
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;2024=(λ&#3627408485;.&#3627408468;&#3627408464;&#3627408465;80&#3627408485;>&#3627408468;&#3627408464;&#3627408465;50&#3627408485;) 2024 &#3627408485;=2024
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;2024=&#3627408468;&#3627408464;&#3627408465;802024>&#3627408468;&#3627408464;&#3627408465;502024 Q.E.D.

Thankstothephoenix,wehavenotneededtolookattheimplementationofliftA2inordertounderstandhowitworks.Still,whatdoes
theimplementationlooklike?Ituses<*>andfmap:
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 f x = (<*>) (fmap f x)
It turns out that in the Function Functor, fmap is the Bluebird combinator, and
in the Function Applicative, <*> is the Starling combinator. So again,insteadof
lookingatthecodeforfmapand<*>,inthenextslidewearegoingtoexploitthefactthat fmap = bluebird and <*> = starling.
fmap :: (a -> b) -> (r -> a) -> (r -> b)
&#3627408437; &#3627408485; &#3627408486; &#3627408487;=&#3627408485;(&#3627408486; &#3627408487;) &#3627408437; &#3627408467; &#3627408468; &#3627408485;=&#3627408467;(&#3627408468; &#3627408485;)&#3627408437;??????&#3627408482;&#3627408466;&#3627408463;??????&#3627408479;&#3627408465;
https://hackage.haskell.org/package/data-aviary-0.4.0/docs/Data-Aviary-Birds.html
&#3627408437;=λ&#3627408467;.λ&#3627408468;.&#3627408485;.&#3627408467;&#3627408468;&#3627408485;
(<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b)
?????? &#3627408485; &#3627408486; &#3627408487;=(&#3627408485; &#3627408487;)(&#3627408486; &#3627408487;)?????? &#3627408467; &#3627408468; &#3627408485;=(&#3627408467; &#3627408485;)(&#3627408468; &#3627408485;)??????&#3627408481;&#3627408462;&#3627408479;????????????&#3627408475;&#3627408468;??????=λ&#3627408467;.λ&#3627408468;.λ&#3627408485;.&#3627408467;&#3627408485;&#3627408468;&#3627408485;
liftA2 f x = (<*>)(fmap f x)
liftA2 f g = S(B f g)
The function composition function. Given two functions &#3627408467;
and &#3627408468;, it returns a function ℎ that is &#3627408467; composed with &#3627408468;,
i.e. ℎ&#3627408485;=&#3627408467;(&#3627408468;&#3627408485;). Also known as Compositor.
Also known as Distributor.

Equation Action
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=????????????&#3627408467;&#3627408481;&#3627408436;2>&#3627408468;&#3627408464;&#3627408465; 80&#3627408468;&#3627408464;&#3627408465; 50 ????????????&#3627408467;&#3627408481;&#3627408436;2&#3627408467;&#3627408468;=??????(&#3627408437;&#3627408467;&#3627408468;)
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=??????(&#3627408437;>&#3627408468;&#3627408464;&#3627408465;80)&#3627408468;&#3627408464;&#3627408465;50 ??????=λ&#3627408467;.λ&#3627408468;.λ&#3627408485;.&#3627408467; &#3627408485;&#3627408468; &#3627408485;
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=(λ&#3627408467;.λ&#3627408468;.λ&#3627408485;.&#3627408467;&#3627408485;&#3627408468;&#3627408485;)(&#3627408437;>&#3627408468;&#3627408464;&#3627408465;80)&#3627408468;&#3627408464;&#3627408465;50 &#3627408467;=&#3627408437;>&#3627408468;&#3627408464;&#3627408465;80
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=(λ&#3627408468;.λ&#3627408485;.&#3627408437;>&#3627408468;&#3627408464;&#3627408465;80&#3627408485;&#3627408468;&#3627408485;)&#3627408468;&#3627408464;&#3627408465;50 &#3627408468;=&#3627408468;&#3627408464;&#3627408465; 50
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=(λ&#3627408485;.&#3627408437;>&#3627408468;&#3627408464;&#3627408465;80&#3627408485;&#3627408468;&#3627408464;&#3627408465;50&#3627408485;) &#3627408437;=λ&#3627408467;.λ&#3627408468;.λ&#3627408485;.&#3627408467;&#3627408468; &#3627408485;=λ&#3627408467;.λ&#3627408468;.λ&#3627408486;.&#3627408467;&#3627408468; &#3627408486;
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=(λ&#3627408485;.(λ&#3627408467;.λ&#3627408468;.λ&#3627408486;.&#3627408467;&#3627408468;&#3627408486;)>&#3627408468;&#3627408464;&#3627408465;80&#3627408485;&#3627408468;&#3627408464;&#3627408465;50&#3627408485;) &#3627408467;=>
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=(λ&#3627408485;.(λ&#3627408468;.λ&#3627408486;.>&#3627408468;&#3627408486;)&#3627408468;&#3627408464;&#3627408465;80&#3627408485;&#3627408468;&#3627408464;&#3627408465;50&#3627408485;) &#3627408468;=&#3627408468;&#3627408464;&#3627408465;80
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=(λ&#3627408485;.(λ&#3627408486;.>&#3627408468;&#3627408464;&#3627408465;80&#3627408486;)&#3627408485;&#3627408468;&#3627408464;&#3627408465;50&#3627408485;) &#3627408486;=&#3627408485;
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=(λ&#3627408485;.>&#3627408468;&#3627408464;&#3627408465;80&#3627408485;&#3627408468;&#3627408464;&#3627408465;50&#3627408485;) >&#3627408485;&#3627408486;=&#3627408485;>&#3627408486;
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=(λ&#3627408485;.&#3627408468;&#3627408464;&#3627408465;80&#3627408485;>&#3627408468;&#3627408464;&#3627408465;50&#3627408485;) apply ??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;to2024
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;2024=λ&#3627408485;.&#3627408468;&#3627408464;&#3627408465;80&#3627408485;>&#3627408468;&#3627408464;&#3627408465;50&#3627408485;2024 &#3627408485;=2024

??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;2024=&#3627408468;&#3627408464;&#3627408465;802024>&#3627408468;&#3627408464;&#3627408465;502024 Q.E.D.
&#3627408437;=λ&#3627408467;.λ&#3627408468;.&#3627408485;.&#3627408467;&#3627408468;&#3627408485;??????=λ&#3627408467;.λ&#3627408468;.λ&#3627408485;.&#3627408467;&#3627408485;&#3627408468;&#3627408485;liftA2 f g = S(B f g)

In previous slides, we saw this definition of liftA2
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 f x = (<*>) (fmap f x)
Here is the same definition, but using the infix operator equivalent of function fmap, and the infix operator equivalent of function (<*>).
liftA2 f g h = f <$> g <*> h x

The above is a more convenient version of the following:
liftA2 f g h = pure f <*> g <*> h
(<$>) :: Functor f => (a -> b) -> f a -> f b
(<$>) = fmap
https://hackage.haskell.org/package/base-4.20.0.1/docs/Control-Applicative.html

OnthepreviousslidewesawthefollowingpossibleimplementationofliftA2
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
liftA2 f g h = pure f <*> g <*> h
In the Function Applicative, <*> is the Starling combinator that we saw
earlier, and pure is the Kestrel combinator. So again,insteadoflookingat
thecodeforpureand<*>,inthenextslidewearegoingtoexploitthefact
that pure = kestrel and <*> = starling.
pure :: a -> (r -> a)
??????&#3627408485; &#3627408486;=&#3627408485;?????? &#3627408467; &#3627408468;=&#3627408467;??????&#3627408466;&#3627408480;&#3627408481;&#3627408479;&#3627408466;??????
https://hackage.haskell.org/package/data-aviary-0.4.0/docs/Data-Aviary-Birds.html
??????=λ&#3627408467;.λ&#3627408468;.&#3627408467;
(<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b)
?????? &#3627408485; &#3627408486; &#3627408487;=(&#3627408485; &#3627408487;)(&#3627408486; &#3627408487;)?????? &#3627408467; &#3627408468; &#3627408485;=(&#3627408467; &#3627408485;)(&#3627408468; &#3627408485;)??????&#3627408481;&#3627408462;&#3627408479;????????????&#3627408475;&#3627408468;??????=λ&#3627408467;.λ&#3627408468;.λ&#3627408485;.&#3627408467;&#3627408485;&#3627408468;&#3627408485;
liftA2 f x = (<*>) (fmap f x)
liftA2 f g h = f <$> g <*> h
liftA2 f g h = pure f <*> g <*> h
liftA2 f g h = S (S (K f) g) h
Also known as
Cancellator.

Equation Action
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=????????????&#3627408467;&#3627408481;&#3627408436;2>&#3627408468;&#3627408464;&#3627408465; 80&#3627408468;&#3627408464;&#3627408465; 50 ????????????&#3627408467;&#3627408481;&#3627408436;2&#3627408467;&#3627408468;ℎ=??????????????????&#3627408467;&#3627408468;ℎ
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=??????????????????>&#3627408468;&#3627408464;&#3627408465;80&#3627408468;&#3627408464;&#3627408465;50 ??????=λ&#3627408467;.λ&#3627408468;.λ&#3627408485;.&#3627408467; &#3627408485;&#3627408468; &#3627408485;
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=(λ&#3627408467;.λ&#3627408468;.λ&#3627408485;.&#3627408467;&#3627408485;&#3627408468;&#3627408485;)????????????>&#3627408468;&#3627408464;&#3627408465;80&#3627408468;&#3627408464;&#3627408465;50 &#3627408467;=????????????>&#3627408468;&#3627408464;&#3627408465;80
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=(λ&#3627408468;.λ&#3627408485;.????????????>&#3627408468;&#3627408464;&#3627408465;80&#3627408485;&#3627408468;&#3627408485;)&#3627408468;&#3627408464;&#3627408465;50 &#3627408468;= &#3627408468;&#3627408464;&#3627408465;50
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=λ&#3627408485;.????????????>&#3627408468;&#3627408464;&#3627408465;80&#3627408485;&#3627408468;&#3627408464;&#3627408465;50&#3627408485; ??????=λ&#3627408467;.λ&#3627408468;.λ&#3627408485;.&#3627408467;&#3627408485;&#3627408468;&#3627408485;=λ&#3627408467;.λ&#3627408468;.λ&#3627408486;.&#3627408467;&#3627408486;&#3627408468;&#3627408486;
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=λ&#3627408485;.λ&#3627408467;.λ&#3627408468;.λ&#3627408486;.&#3627408467;&#3627408486;&#3627408468;&#3627408486;??????>&#3627408468;&#3627408464;&#3627408465;80&#3627408485;&#3627408468;&#3627408464;&#3627408465;50&#3627408485; &#3627408467;=(??????>)
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=λ&#3627408485;.(λ&#3627408468;.λ&#3627408486;.??????>&#3627408486;&#3627408468;&#3627408486;)&#3627408468;&#3627408464;&#3627408465;80&#3627408485;&#3627408468;&#3627408464;&#3627408465;50&#3627408485; &#3627408468;= &#3627408468;&#3627408464;&#3627408465;80
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=λ&#3627408485;.(λ&#3627408486;.??????>&#3627408486;&#3627408468;&#3627408464;&#3627408465;80&#3627408486;)&#3627408485;&#3627408468;&#3627408464;&#3627408465;50&#3627408485; &#3627408486;= &#3627408485;
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=λ&#3627408485;.??????>&#3627408485;&#3627408468;&#3627408464;&#3627408465;80&#3627408485;&#3627408468;&#3627408464;&#3627408465;50&#3627408485; K=λ&#3627408467;.λ&#3627408468;.&#3627408467;
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=λ&#3627408485;.(λ&#3627408468;.>)&#3627408485;&#3627408468;&#3627408464;&#3627408465;80&#3627408485;&#3627408468;&#3627408464;&#3627408465;50&#3627408485; &#3627408467;=>
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=λ&#3627408485;.(λ&#3627408468;.>)&#3627408485;&#3627408468;&#3627408464;&#3627408465;80&#3627408485;&#3627408468;&#3627408464;&#3627408465;50&#3627408485; &#3627408468;= &#3627408485;
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=λ&#3627408485;.(>)&#3627408468;&#3627408464;&#3627408465;80&#3627408485;&#3627408468;&#3627408464;&#3627408465;50&#3627408485; >&#3627408485;&#3627408486;=&#3627408485;>&#3627408486;
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;=λ&#3627408485;.&#3627408468;&#3627408464;&#3627408465;80&#3627408485;>&#3627408468;&#3627408464;&#3627408465;50&#3627408485; apply ??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;to2024
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;2024=λ&#3627408485;.&#3627408468;&#3627408464;&#3627408465;80&#3627408485;>&#3627408468;&#3627408464;&#3627408465;50&#3627408485;2024 &#3627408485;=2024
??????&#3627408466;&#3627408462;&#3627408477;_&#3627408486;&#3627408466;&#3627408462;&#3627408479;2024=&#3627408468;&#3627408464;&#3627408465;802024>&#3627408468;&#3627408464;&#3627408465;502024 Q.E.D.
??????=λ&#3627408467;.λ&#3627408468;.&#3627408467;??????=λ&#3627408467;.λ&#3627408468;.λ&#3627408485;.&#3627408467;&#3627408485;&#3627408468;&#3627408485;liftA2 f g h = S (S (K f) g) h

WehadagoatunderstandingthefollowingfunctionsoftheFunctionApplicative,withouttheneedtolook
attheircode:fmap,pure,<*>andliftA2.
Wedidthisbylookingattheirequivalentcombinators:Bluebird,Kestrel,StarlingandPhoenix.
WhilewehavenowseenthecodeforliftA2,wehavenotyetseenthatforfmap,pureand<*>.
Nowthewearefamiliarwiththecombinators,thecodeforfmap,pureand<*>doesnotpresentany
surprises,andcanbeseenonthefollowingslide,whichactsasarecapofthecorrespondencebetweenthe
functionsandthecombinators.

instance Applicative ((->) r) where
pure x = (\_ -> x)
f <*> g = \x -> f x (g x)
liftA2 f x = (<*>) (fmap f x)
instance Functor ((->) r) where
fmap = (.)
B
(<$>) :: Functor f => (a -> b) -> f a -> f b
(<$>) = fmap
Starling
Kestrel ?????? &#3627408485; &#3627408486;=&#3627408485;
&#3627408437;??????&#3627408482;&#3627408466;&#3627408463;??????&#3627408479;&#3627408465;&#3627408437; &#3627408467; &#3627408468; &#3627408485;=&#3627408467;&#3627408468; &#3627408485;
(<*>)
pure
fmap
(<$>)
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
class Functor f where
fmap :: (a -> b) -> f a -> f b
K
?????? &#3627408467; &#3627408468; &#3627408485;=(&#3627408467; &#3627408485;)(&#3627408468; &#3627408485;)S
Phoenix liftA2Φ &#3627408467; &#3627408468; ℎ &#3627408485;=&#3627408467;(&#3627408468; &#3627408485;)(ℎ &#3627408485;)Φ

The next slide shows the Scala code for the definition of leap_year in
terms of the following alternative equivalent implementations of liftA2:
liftA2 f x = (<*>) (fmap f x)
liftA2 f g h = f <$> g <*> h x

liftA2 f g h = pure f <*> g <*> h

for
leapYear <- List(leapYear1, leapYear2, leapYear3)
_ = assert(List.range(2000,2025).filter(leapYear) == List(2000, 2004, 2008, 2012, 2016, 2020, 2024))
_ = assert(List(1600, 1700, 1800, 1900, 2000).filter(leapYear) == List(1600, 2000))
yield ()
extension[A,B](f: A => B)
def`<$>`[F[_]: Functor](fa: F[A]): F[B] = fa.map(f)
importcats.*
importcats.implicits.*
valgcd: Int=> Int=> Int=
x => y => x.gcd(y).intValue
val`(>)`: Int=> Int=> Boolean=
x => y => x > y
valleapYear1: Int=> Boolean=
liftA2_v1(`(>)`)(gcd(80), gcd(50))
defliftA2_v1[A,B,C,F[_]: Applicative](f: A => B => C)(fa: F[A], fb: F[B]): F[C] =
fa.map(f) <*>fb
valleapYear3: Int=> Boolean=
liftA2_v3(`(>)`)(gcd(80), gcd(50))
valleapYear2: Int=> Boolean=
liftA2_v2(`(>)`)(gcd(80), gcd(50))
defliftA2_v2[A,B,C,F[_]: Applicative](f: A => B => C)(fa: F[A], fb: F[B]): F[C] =
f `<$>` fa <*> fb
defliftA2_v3[A,B,C,F[_]: Applicative](f: A => B => C)(fa: F[A], fb: F[B]): F[C] =
f.pure <*> fa <*> fb
import scala.math.BigInt.int2bigInt

That’s all. I hope you found it useful.
If you would like a more comprehensive introduction to the Function Applicative, consider checking out the following deck.
https://fpilluminated.com/