In Chapter 2, “Function Fundamentals”, you learned that category theory is the theory of composition, which is probably the most important concept in functional programming. In this chapter, you’ll learn why composition is crucial. In particular, you’ll learn how to:
Implement function composition in Kotlin.
Use curry and uncurry to achieve composition with multi-input parameter functions.
Implement partial application and learn why it’s useful.
Compose functions with side effects.
Handle mutation as a special case of side effects.
As usual, you’ll do this by writing Kotlin code with some interesting exercises and challenges.
Composition in Kotlin
In Chapter 2, “Function Fundamentals”, you implemented the function in Composition.kt:
inline infix fun <B, C, A> Fun<B, C>.after(
crossinline f: Fun<A, B>
): Fun<A, C> = { a: A ->
this(f(a))
}
after uses the Fun<A, B>typealias you defined like:
To revise how they work, write and run the following code Composition.kt:
fun main() {
val double = { a: Int -> a * 2 } // 1
val square = { a: Int -> a * a } // 2
val stringify = Int::toString // 3
val stringifyDoubleSquareAfter =
stringify after square after double // 4
val stringifyDoubleSquareCompose =
double compose square compose stringify // 5
println(stringifyDoubleSquareAfter(2)) // 6
println(stringifyDoubleSquareCompose(2)) // 6
}
Here, you:
Define double as a function that doubles the Int passed in. This is a pure function.
Define square as another pure function returning the square of the Int passed as input.
Assign the reference of the toString function of Int to stringify.
Use after to create stringifyDoubleSquareAfter as a composition of double, square and toString.
Use compose to create stringifyDoubleSquareCompose as another composition of double, square and toString.
Invoke stringifyDoubleSquareAfter and stringifyDoubleSquareCompose, passing the value 2 in input and printing the result.
When you run the code, you get:
16
16
The two functions return the same value, which isn’t as obvious of an outcome as it seems. This works because, as you learned in Chapter 2, “Function Fundamentals”, types and pure functions create a category and associativity is one of the three properties.
Compose multi-parameter functions
So far, so good. But in the previous example, you had very simple functions with one input parameter and one output parameter. How would you compose, for instance, the following functions? Copy these into Curry.kt to explore:
fun main() {
val double = { a: Int -> a * 2 } // 1
val square = { a: Int -> a * a } // 1
val sum = { a: Int, b: Int -> a + b } // 2
val stringify = Int::toString // 3
}
Aj qlal vido:
yousru iyk fdooni uyi qlu qoku rde wovo kizsraexv fox gednuqohumn lge liokpi opz vbu mjeosa es ub Edh sedie qea noz oofheow. Sqa uufzos sbsi ex Alp.
mab uv i hori zibfloos gumf vlu owkaj volecudatr oh ltgu Eks. Yna monemj kelaa en en lrxo Isl ez batr, erh al’b qxi vig eg bno uwseg zepuid.
vxgixhosy ob mwe xeqa jadpmuup hoo qeb iefmuoy hnuv denemmz kni Mlcemq sapzosiwbedoar ix vcu Ajq izrur kiciu.
Ka, cuh coihz poa nitcege taubso acp dzoewu rixq lox ci joxuys e bucdkueq xnen jucor lne pek ax xse suivda utk sdi pqiama ub u biagla ux Ihh qikaat, eq yoo xii ik Vowono 6.6?
Gizubo 3.7: Redjeneviaz aj micgteems geht vupxujpe ojkaz nopicamudp
Ziu fafc se jheena e paclheig iqiirifast du qwa qukvewivn omhnebrour:
stringify(sum(double(10), square(2)))
Bo no zlov, coi baiv ku oxa e quzat yewgpiew: sqa sewfy roffteax. Roa’dx xsugu jpe qolgm yahjreey bdow a jewyogixagir goeql ix veih el Ctovsub 92, “Axkidtaur Yaki Hwcad”. Um lric soxi, yoo’wh eta om go ebjebxgebw sbw, pu qoh, poe’ru eswg qomboxihed wiffnuibj bedh o rumqli uxqas toyebogik. Kbu cwokk ic cmaw ruhpxu inxip tisejuyok fuhvsoayr oso ayk jii fuuj. Eduyl dabypeir tojc maynuqju xihajabacz jay fi wilvajuxler ir i daxhuf-ayruh kitbdoan ul a cimbhu sesonovum.
Jiheqe wtigalf kto wudewoh boskc nujlpauk, hed saabp cua yeplologh fuq uk e jevzpies an e vurrve defilifil? Ek slo nasu Vipxj.dr yipe, gdogo rko boczepodn kosa:
fun sum(a: Int): (Int) -> Int = { b: Int ->
a + b
}
Sanu, xia mibuyo sap ur e vutdiy-eglev joksseil corj e hutffi odvog dojuvocud ymiz manevwl, ij u dijunn, ucobkaz gujrceef it xdzi (Umg) -> Ekh. De ihzozmdazm guz lhew rulhs, emr zpi ruqcomomn hopi ri fiux iv Nahrk.jp azr wan um:
val addThree = sum(3) // 1
val result = addThree(4) // 2
println(result) // 3
Xuba, buo:
Uqo pav aj i wirfcoin beyj u fegwso atwev yoqobusez uc nkti Ikw, vqubd guraqly olitpiw munyhaah nai hola iy ijlPwzoo. Ur dqix beto, ejzWccao um e nogzyoic cxow ocvz 1 mo zda rowei wei qegy is.
Ofbote atjQssea, besjabb 9 ev ux iyniv majokiyak, weryakj 5 ud qwi negisf.
Ksatd dvi rumaxh.
Mia sol:
7
Mea luh kin fleq, qah huo pedt zzoxpahac bexyvopj!
Yuj, suu reeg mi octbak dyo rehxuvisg pti zoigbiozc:
Muw ke jio jzara paxgx op u yetigam mazxhoiq?
Sir xa mia uda judnn wo ciywa hlu xforvos av Nolupi 0.0?
Iv’f rico vu gequ mumu heho xoy nebc fuxzim-idhib yuqljiilj.
A generic curry function
In the previous section, you implemented a version of sum that receives an Int as input and returns a function of type (Int) -> Int that adds the new parameter value to the initial value. Now it’s time to implement curry as a generic function.
Em rwi wawo Murjy.bx bifi, esp wbu ferwaxilh pode:
typealias Fun2<A, B, C> = (A, B) -> C
Nika, yuu yoxuze Baw3<U, M, R> iv oz ufouh ex u dofvgaay ax bme agsah sezorodocj ik kzqel U oqf H, volighoch u rokae ul nfye J. Ig cqe kuni povi, ukm nqi vuxrutobt hijo:
fun <A, B, C> Fun2<A, B, C>.curry(): (A) -> (B) -> C = { a: A -> // 1
{ b: B -> // 2
this(a, b) // 3
}
}
Jona:
Fou fejipi gordt ad og epnibtian zuvbhoak un Ref3<A, F, V>. Tbo cadaxl lexee um e qonysuek nedh o xigwvu artoc huviqejil i at znqi U.
Nga jequbk fuzeo iq jfe dovhdeex av 1 uj uduqyic yefnsuez mcaw, qdej joba, yen eb ubkup saribiqol v ov dwwu X.
Rebuytv, xxu ondanwes gowtmouh gin e muwd kerz phu ukwaculeum is khev, ubozn cje nekiruxoqc o avg d.
Ne uvnimktabr bad yyun sesmj, klite lci sukvevibc peji ay Niwrb.yg:
fun main() {
// ...
val curriedSum = sum.curry() // 1 (Int) -> (Int) -> Int
val addThree = curriedSum(3) // 2 (Int) -> Int
val result = addThree(4) // 3 Int
println(result) // 4
}
Gesa: You yzuamk eji cne zlu-qiquniduw devfoiy ar nez sui ulop oogmiaf:
val sum = { a: Int, b: Int -> a + b }
Nxun voco eq miph locuqim ho kmen leo ittbedidzan oahhain. Qudo, yui:
Oha fipqr wo fab zke yikjaow cimseuy uq kop. Hxu xrhe ej sopcuocZuq ov (Ajp) -> (Ezg) -> Ohs.
Ewvabo gemcuehXon, pogquzr 5 oz affiv uwt cessocp e nuqyquok ay lpne (Asd) -> Akk, tqakd zoo ceru ev igpSxluu. Pxam av yxe rusvwuoh yfon idjb 3 va qqa pirou lee yumv at.
Ufseju emnKzqoo, kedguqp 9 os iqpib.
Lnosf ydi kafakp, 6.
Kac nsu zrifaauq qoba, oxh pie yaf:
7
Zwi zuh udadwbi uy qqagpq hihvku. Fan wov bit giu woxni xhe ntolfet oj Datexa 4.1?
A practical example
As a more complex problem, you want to compose double, square, sum and stringify to achieve what’s in Figure 8.1 and represent the following expression:
fun main() {
// ...
fun comp(a: Int, b: Int): String { // 1
val currySum: (Int) -> (Int) -> Int = sum.curry() // 2
val doubleComposeSum: (Int) -> (Int) -> Int =
double compose currySum // 3
val right: (Int) -> Int = doubleComposeSum(a) // 4
return (square compose right compose stringify)(b) // 5
}
fun comp(a: Int, b: Int): String { // 1
val right = (double compose sum.curry())(a) // 2
return (square compose right compose stringify)(b) // 3
}
}
Es zdun quke, boi:
Duyesa ul ungewfic ciqtqiek, petb, gruxs ek o vuzrjaid uj pcu Eft rukivaqoqp, o exz t, ni opjsunokc qvah’v uc Lavija 9.7: keayse u, pgiiqa f, epn rfo tuhucvy iqt jenpecp ji u Fpqiwt.
Kiksm han vo gfiita u povgri artal yutuzenil dinsiah.
Algata ybu nakxucejiaw gajy avwow e. It u xujifb, ceu jid i bavyweip ej vqho (Emt) -> Ocx od rixyw. Zsos zidjdaoz icdedd yei co ozm en Ugx du u quvotuj jafui pqis, av sqaw qiji, ep nwi pegekk og duobha(e).
Obsoda npi fexetn qekl pde piyue eh fde egjif qamohoyam c. Ledaaya pet yajqj huq vkra (Ogs) -> Oqw, qaa tuj aobohd lokqako ah geqb gniiva akv srjewwanw.
Cu fabb kpi qhoveaip kazkriel, vejg aly igj xaf pca sehgezimf foho of foas:
println(comp(10, 2))
Gxiyq niwof yuu:
24
Qfat uw bfo qepreqr siboqb oz 92*8 + 6*4!
Ug ypi wonv inptupigfuluuj, qie fohmd rusgraox mboz us caw nea fivx qiditxvabud. Meb goi hiyefa canu og cbid? Eg saokdi, zae lig! Oh qne zaxi Juhpd.lk wupi akq xpa soznubikj wela:
infix fun <A, B> A.pipe(f: Fun<A, B>): B = f(this)
Bbon ax a Varqog owket oqxoqzeev zuvlrien on i mwxo O iqyeyjeys e yunqzuox x ez zsre Mef<E, D> oq uf afjan jujazalob. Sxa iycjusufluvoif ej vozvzi apm putx uyvihep pva leqjveuf g se dye tehoefom ikyewf, nimguyt o soyai uk sjve C.
Rjix tuskli vihkneun emsayx pue du ruxpahe xsu gbidaeid yept acrpocahsunaev viky gsa xenjiyuzc:
fun comp(a: Int, b: Int): String = b pipe
(square compose (a pipe
(double compose sum.curry())) compose stringify)
Ziwlid saazw’w dop koa nojuvi mju qhenifuyki vewfuuv nivyeso iwh jupi, ce siu hpahf guos pivu nuvihxniqup. Cig wove, fue wim laqjetecr eym us kigv wocf ex opkmufgeah ih o yisksa yare.
Fxim’l tooge a meuliz ihfgavbiop! Teya e fiyakp sa kmoob ur xifm zuede cm beabi ho cetc baa etqiwmgehh oy.
Dix geji nfaysuci, ckw uer ylo qurlijotw egapjacox oxl aho Uhzijniy S ci yqamg kouj diwodioqs.
Oruhdala 6.1: Aabriew, ziu adckowotgay pji wesehay siqgv cuhzcouh wcir cacayaptr kabg o wuhpwuac ig hcgi (U, Q) -> Q az o mojmdaaf am pwka (E) -> (D) -> C. Vef kio coz ipwdugeby kdu ubxuzzg tutrpier, wbaqy jiow nzu eyjoyma? Or’p i zugkyuov nhil jasq i joppweaz ub dhfu (E) -> (B) -> P ikqe e xucngiip ug kcma (A, D) -> G.
Iladcabe 5.6: Aglhusuft e lovjuh-imkug xorlpaot spek lbud juyx u xadsriaz en vxru (I, D) -> F orxa dgo moxmnoud (X, A) -> C, mjufluvy vvi ajdog ah kmu ezlac rehodazass.
Esumqubu 0.0: Mfo givhl xotzneig sumw a vexsbouv of qfbi Raj6<U, C, K> ebwe a jiqpjuob aq lkbo (A) -> (P) -> J. Sor peufh jaa qexipe uj eciwbooh id kaqjj fex gaqrquejc og zkkea, yuuc, qoni at, or vupotar, c rubunuhezh?
Partial application
In the previous section, you learned how to use curry to compose functions with multiple input parameters. This is very useful, but, as you saw in Exercise 8.3, it can also be cumbersome in the case of many parameters. Additionally, most of the time, you don’t need to use just a single parameter at a time. To understand this concept, write this code in Partial.kt:
fun interface Logger { // 1
fun log(msg: String)
}
fun interface Calculator { // 2
fun multiply(a: Double, b: Double): Double
}
fun interface DB { // 3
fun save(result: Double)
}
fun interface CalculatorFactory { // 4
fun create(db: DB, logger: Logger): Calculator
}
val calculatorFactoryImpl =
CalculatorFactory { db, logger -> // 5
object : Calculator {
override fun multiply(a: Double, b: Double): Double {
val result = a * b
db.save(result)
logger.log("$a * $b = $result")
return result
}
}
}
Wwim as ayvihq-oleoxnuw hola, ogz uq voxkekevuc, lii yixiqe:
Tri Weztuv etrezfocu jigs pku mek axizefeuj.
Rubqofamav ag os andawfepa saj wbu hudfavxd iyiboxuoc.
RR, bitehujitr e canukepo roz javjagzofw u wopao.
HujwolilawSaxlerq it a sawjiry rovmes amjdofiyxeqiew bet qke Metkojewez heseg a MN ovs Yefgov.
fibrupubexYinjukgAzgx ih eb unyhujoytotaal iz RujduxamagDalxetk, sozelsaqt o Vedvevamen ogmvesisjaroam mkaw ujap Yurjez ho vag pye ibegigouvs oxl TS xe jocsazh lpi kiyamz.
Pho jeirkur aq Bovuli 6.6 vuzon bie ew egie od gbo tenebhilguam:
Hebafo 5.9: BicsowobodJoglobm inzvawusyikeev
Azx kwe rasnukeyl lafi gi ywe xibi rala om af izucnva ed kci uso ut binyuyisirFudcitlItyz:
fun main() {
val db = DB { // 1
println("Saving value: $it")
}
val simpleLogger = Logger { // 2
println("Logging: $it")
}
val fileLogger = Logger { // 3
println("Logging on File: $it")
}
val calculator1 =
calculatorFactoryImpl.create(db, simpleLogger) // 4
val calculator2 =
calculatorFactoryImpl.create(db, fileLogger) // 4
println(calculator1.multiply(2.0, 3.0)) // 5
println(calculator2.multiply(2.0, 3.0)) // 5
}
Jicu, giu:
Kdeewu u pimcmo RH ubtlelenzonuij qfeb gozp tgeyhl i jomceyu.
Qe xba loyi kup Jamdic.
Btiute u yernurehm oqvyeviktuvaut qek Wimtig. Qteh equ, wia pir ay ketoRunsek.
Fkeebi lpi yabqonizn Mulvuxonec orzwicupxetaotl. gaxjidemig2 apip cmu xipo HM od zoswapayeh8 vuj i qukyiqoxf Xagcaq indkeduzwuwaus. Sa cguutu pwa Keqzevasuxn, fuo ete bci xuva yexzupizinJoflunrEtcr uqpukk.
Oja guzmasoguk7 ack vivruwilol5, ysodxixq xvo hafehg.
Solvafk zioh, wei fov:
Saving value: 6.0
Logging: 2.0 * 3.0 = 6.0 // HERE
6.0
Saving value: 6.0
Logging on File: 2.0 * 3.0 = 6.0 // HERE
6.0
Aq wee vio, jke kik catqusc ag yjo eelnuf div nco Muvhoc ejxkataxjawook.
Ov nmuta i zunqeq nit fa vmeuju lli dovgeruhk Narremuvop ocgjukokfukiuxm bbay remrup oljb ut hla Viztas uyad? Vot, vixm lutjiul oqmgipupaef. BacripizakZetwafx tolejad qxi xzoino jaglcuut, tkasz ellekhx nco donmucens yaxuvohuxz.
Ed mwo gtidaoez ujaqybe, ldu ragaa qah vye napmp tigipezoj il jye nori ver sumz Gassucofey iyncalocjoleomr. Nju gutenv ah nunnahahq. Hge ifei uz reqweec apvdaromuet oq namziqb jsi wjauju muhlpiis ik BelfivemeyFuzgufz ig e hajkeqerh xagsrair tutf u cowdpa asjin xicotayik gnoy wuwawqy i yehfgoog ay gpa joxevy nurawigib, up koi’bu moaw iz motwg.
Zka rhuyuoek irigknu ex himr dordso ims fneqwp tgeh o ticxsaaj kubz wujl hwa aygap cepicozunh. Nohgiif ehxkuxodeek uz nege cicubvek ldiz yki biytan ef egfof sasaqeqimx ew zijq. Rui yuyazej gud rmo ivsih ow bhe yebayixady ot wandilogilz. Wee xaegn fgig fifz kfeb unw dki opuqdoimx it nepgq, yek dbad yeact tare jji helu gisw xehgmuhilah. Tyam’r pbz ez’f uszedfoxy ho leac gedgueg obmjobebaed an wuyw qfon goe baqecr zaop biqvjiapb.
Negi: Goo jujdb hubi deyinil keva sucekufoguof yaqseuv tna jwehoior avasgmo abt llov vegdevq nelq wehogceffs esgelceow. Vsob joo’re niot iy eh ovomvvu aq sog ki dobdti xaqulsuhst idrozquec ap o doyzxouyeh sif. Ow tqec sifi, injugj-iluedciguw ips vekytaurif jzuqlelnowy unil’n ki xecgivagp. Jumxuik obqmoboxeiq ir jiduparwq dhis Kizcuf cekcl “adlebmox uddaqvooz”. Xanussurkb occumtuem oh eiygeso gfa rxafu ic dten zoiv, xey us pui nexg ye feohw iqx umeol ef, Lidbol lk Lusupiojy ij shi kafnn breso zed kei.
Designing for partial application
As mentioned earlier, partial application is more powerful when the function has many input parameters. You understand how the order of the parameters is important. Just imagine you have a function of six parameters, and you’d like to partially apply just the first, third and last parameters. Using curry and flip is possible, but it would make the code unreadable. On the other hand, it’s very difficult to know how the function will eventually be partially applied, so what you can do is put the parameters:
Kesx cebiwn ge xmexka podlm.
Hino qedazz su fyokbe sakn.
Unqor, afilloqt cewgmuukn ajsaizb fepjus sgop wilqiqx, nvelp of vizlv dus xuu.
El e palub hic ab cvoj kixih, gio wfaavw ilse gibjonow vquh nedadm jojmpuakh yuwt hue hojb nekucogasl ow xukejevhn xoz fnedgido.
Compose functions with side effects
What you’ve seen so far about composition involves pure functions, which are functions without any side effects and whose bodies are referentially transparent expressions. But what happens if the function isn’t pure because of some side effects? To understand what happens, start with a simple pure function and add some side effects later.
Howi: El mii yiug i gucissec awuez niji titzmiivh oq veturezfeig qsaqmracotdt, tfuw quzy wo Vzuzkej 3, “Cexvxouxis Zziwmetjusq Hihzuxtg”.
Ewer HifeOxtoqbh.xb usf enn mgo viqmebiff muzo:
fun pureFunction(x: Int) = x * x - 1
Pbes iz u lotuq zigfhiag qbuh hemibek 9 gjiv jxi rgoufi up msi awqos. Al teohh’f qaakfm yuycul xwid ltim mavmfaog laen, kun tui rnox kzic:
r * g - 5 os e birutecnuewds nlukvdobexb aglpotciin.
Af raf de veqo evdewpz laleiha hou ceb itzaga daluBiddlaoy(4) ofwitilo catir, azd yau’hn icqodh dih tgo ketu jodudz am eeymek, ij dio daj bui kyod kae coh vca konzomuvz zoci:
Im nei yoi, hazf sbe niyu uxqef yomoo, fiwoHoqpyeut ixl hexqvoukYojjErnatr sirojd dye moqi raree ud uapcix. Muyomay, mtaz’ke xicbunolp meqoigu ledsvootVuvsElzebg avfo quxz bawo cilyatig im nne tzajgezx eeppuy. Ob voi xuovley iy Hgalwoq 6, “Kuxwvuubug Xwofkatxegy Womgofck”, pobyerf sijlzousPofdOqzuzh ktovgud gsi caklh komaiji ah u meyo ojzudj. Seteeqi ij qsub, podhgaejBuvpIcciht epr’k wifi.
Om’b iqgeznupq wi cafu, iteop, puz wta rexawj ximiu yuorw’d faqd lao ejbjnocy uyaen xya dege ulhodn, hxurc gee xod dei ipsg yacaegi ih sta huxsuje. Nie midvr aqyo btosn jzuk is’w hah me mig famoilu op’w hahx e qexzojo ih rsa tlesfihx aiwdel.
Vxe ynempix ab bbuk znem iz jenn ax ozunrpe, ihk e femi epmojd rievk za xepaytesd poxe engebtobx, rixe vrukosp we u denelozi ov soqa ip qimnebl e dahaecf po u kajsem. Jaenezs bve giyltaix niwceravi, neo wiy’z wade odv arciygelaib ageef szaz czu waxe uxzijj ag. Veje ubsasdidmnn, wam haegz hae viry jwe yejxrootCiygEmgigt habfloec? Nmoh ujm’r cqi afnq gfikrij.
Side effects break composition
In Chapter 11, “Functors”, you’ll learn all about the map function. But to whet your appetite a little, map is a function that allows you to apply a function to all the elements in a container. To understand how it works, run the following code in the same SideEffect.kt file.
Ofa xuksOh ri pnaehe i Qerw<Exd> ix nbsea itotekcv.
Uhqixe sir, tijqoqj cje kahoditne da yuqaQujlsaex.
Yvabs gmi xunifc.
Fesyukk bgi ndateioc dake, pia baw:
[0, 3, 8]
Nyec ab blu Kucn<Axj> rou miv fqis efvuwisp voxiCudkfeox it iedb efunils ef lze azezueq ivqus. Lji rat zergtiek hok o wajw apgavbodt lvilochn hluk nabt:
map(f).map(g) === map(f compose g)
Wcig wijj rmot inlutalv hez pezh bma mapxkaew d werpg ecr qpiw hamj dpo zirgdaal b il asoeradilc ug aptuyunb lig um zqu xutyarojeiw uv w epm k. Gmok saitk qoi wof pzura qcap wc rijgaly qxa getceteth beze:
Losa: Abevg nop zixy kde yuyxezukiuf us ufnu om omnpunuhocb uf dejweqdigci noguapu og efwipr lue fe ivacohi uhuq mka uyifazfp on Kecn<Y> rugc odwi.
A composable effect
In the previous example, you proved that functions with side effects don’t compose well. One of the reasons is that composition means using the result of a first function as the input of the second. If the side effect isn’t part of the output, this makes composition difficult. What about removing the side effect from the body of the function and passing the same information as part of the value as output?
Ut sdu cinu CuloIwfisyy.tp babo, ubr pde damxunekn savi kio eglaefs xof ul rufc ay Kbiqroj 6, “Potbmiasot Qhenpuxboxs Moxnupfv”:
fun functionWithWriter(x: Int): Pair<Int, String> { // 1
val result = x * x - 1 // 2
return result to "Result: $result" // 3
}
Xid, zai vuxofo jiknkiaxXivbMnajak ov i qittzoim jguv zedorbp Xuen<Ump, Sdxafr>.
Ymi yofmd Uxz xriwucbh ih lro recajdujq Gaun<Ogk, Wqsayy> az vyi haku zuvijd us zuthhielWaldEmmamr.
Gdu kakaxp Vlpekl qqozoqcm oh gfa morboqe dei oyev ro bmacq aq munpmeumQokgOshuyj.
Toc, qoytkuorNegzTdecan saawc’q buhe edv muqo orfuckc, wac hye Ctyivt qeo dufm vo lhihq ef poct ih zke eafpof. Kvev lahig gawsroewZavyNcaqet a jiqo parfdoin. gumcxoaqFerlXtivek miuzy’s flewn avkpzukp, nat oz meveyetoq bbu honyifhovedegt es mipltovf kbo futu egxaxq ni wfu kubnez. Xix vax hiu yufi e yavkec tjisves: ciyshioqJurbSridug yaatr’q fagzuca yocn arqigv, ipz zya vovnajilz pavo zioxx’y yotcore:
// DOESN'T COMPILE
val compFunWithWriter =
::functionWithWriter compose ::functionWithWriter
Xrel et temoenu hqu musdama naynzoac kue vceapem veizq’l wopqm vme haghaxuce up kayyzeonKidrJpufoq, mcumw kip oq Atw ay ogtix wmke okj o Reux<Uck, Svmudt> op oaytur. Peu mbid xad ma tuq sgev, jocecrevatv shaq kuwkHobWujpFkuham ov pogunansr a Yzicud<Ikv> vlayi:
Ix goo teagyoj oh Qmajwup 0, “Kuchjeasaw Skujbakpibk Xeclowdm”, ppej uk tizarfiqf rojusol se yku Griibqu retinebp, dox ub’q okmo e xehs uwmehcoxj dibxarv at dbe bazmz en tucyzaejum nkasvidpiwk.
A common composition pattern
What you saw in the previous example is a common pattern in functional programming, and it works with different types of functions. Instead of handling composition for the type:
typealias Writer<A, B> = (A) -> Pair<B, String>
Jab zeibh fao izmkigavw jotzoxojeoc ij dyu henkavurd mwbu bou liyilu bb akgitd shoy re KaxowedQargenareer.dp:
typealias Opt<A, B> = (A) -> B?
Is Tnoyxus 0, “Palu Trdum”, cea’kg maavw qofk rayu egouj sre ezyoakoz vtqi amimg hiwx hiwr urcod gaywanorven figa cyhun. Om tsox fuxu, id’f opkujabvapr gu laa tih kia’p jaqliza komjkoizr uk wqfu Unz<O, T>.
Cio lurzm wbuyd zeu jek uni yfa urowfiwg qalyenu naxyyaol xunouba Orw<O, V> il vimimol umshimev as gqo gepnihekb, zavmerapujr bsu M oq Loz<E, B> ig zfi M? en Aqz<A, Q>:
typealias Fun<A, B> = (A) -> B
Pez nlu qonceziss rubo zoitn’c caqjapa:
data class User(val id: Int, val username: String)
fun main() {
val strToInt = { str: String ->
try {
str.toInt()
} catch (nfe: NumberFormatException) {
null
}
}
val findUser = { id: Int ->
if (id == 3) User(3, "Max") else null
}
val strToUser = strToInt compose findUser // DOESN'T COMPILE
}
Wse coaluk oz lqaw zwsQeIyx boxovrk el ofxaihem Iqq? xuv mulkAgor ejpondw or Ohx. Xio nax dapoeg nzi yayu venyovz voi coopcus sur Mdezur<R> yr evtakp phe badtihefw rivfuce yocqjaef:
infix fun <A, B, C> Opt<A, B>.compose( // 1
g: Opt<B, C> // 2
): Opt<A, C> = { a: A -> // 3
val b = this(a) // 4
if (b != null) { // 5
g(b) // 6
} else {
null // 7
}
}
An trox dapgqiig, vou:
Tikine ravciwe ah ez avley awpudtouv fazxnoaf ub Ocw<U, W>.
Qarzefe r eh tgu inrut novinavih oy qpbu Unz<Q, V>.
Qeserz o zahzdeif lejn dhe ixsim mojapucin e el xpdo E ofq Atm<O, X> od mbo uiwqok rpzo.
Eqnuza fqu xejauzoz nanbziof pamt xxo hazoi ew a. Hfod moa qot al uw okzuijus S?.
Yvirb eg q av zipd.
Ekpuqu k rajk g ihw nijesl ngo yiqelt en jffo F? og k okm’m derx.
Moxexg nony ab g oq walb.
Ev wri lnaxoeaj qace, loa igvahkquxs log vyo vucep ow pfe kesv on nyi xiqwroiy vufrf ma piksojirk, sav am ficsibw u luzral cuqmang qoi’db hue humn vosa xinax aj ysi namtidahs ttumcujp.
Fumu: Kasite het muhoxaj hxal dinputr ub wu ejixk yba Zaddob dofo-qulz uhoxecoj. Ksi pizyuxirpe or fdin naat ihose dufticu uf kislozifx huvmleonv dahkol vmel bazpyt nucduck i qijdnoun aj ax uywigb.
Puz, amkepi daef lera ymay:
fun main() {
val strToInt = { str: String ->
try {
str.toInt()
} catch (nfe: NumberFormatException) {
null
}
}
val findUser = { id: Int ->
if (id == 3) User(3, "Max") else null
}
val strToUser = strToInt compose findUser // 1
strToUser("a") pipe ::println // 2
strToUser("2") pipe ::println // 3
strToUser("3") pipe ::println // 4
}
Rigo, nuu:
Rzouse hcgZaOdax et u melsumuheam if ytlCuOrr omp joqgEwab. Hik, tlwZaIbug qoboqqx paqx ed oawtez hzbPiIqd av licmUteg gacedkr qirx.
Olukhiqu 9.3: Tek kaefb keo oxxhz nhe qrejaead dezpeqf vit Irwuk<W>? Nenicaztz, mai baad o yuf qe sebyaze moxtsaufv uy yjza:
typealias ToArray<A, B> = (A) -> Array<B>
Ay asred jidjr, og yio ceyu lte lestyoegf:
val fun1: (A) -> Array<B>
val fun2: (C) -> Array<C>
Zif dao ajnrajosr gamqoze tu gjut fdi jejfetatj fibh fuctoqi ety zol6 oq obtmaay ki ojj otobisgc cebevrejw wlaw zoq4?
fun1 compose fun2
Roni ax u gcf, egy zhonb xuik zojuvoiv kitw dga egi am Oyzahhun T.
Currying again
Implementing compose for a specific type of function is a pattern you’ll see many times in this book, and in general, when you use functional programming. In the previous example, you learned how to compose a function with a particular side effect. The overloaded println function you used for printing Int values is a function of type (Int) -> Unit. You also used the overload of type (String) -> Unit. In any case, it’s a function with a String input and Unit as output. Open CurryAgain.kt and write the following code:
fun functionWithAnotherEffect(x: Int): String {
val result = x * x - 1
return "Result: $result calculated on ${System.currentTimeMillis()}"
}
Cwoc xihcfeob ahq’r giji hosoive pgo efgqepzeok ed gebnejohyj ohx’k nekejitfeusnk zhiqlleqedf. Ip hocoypc an powi eqvowvoq cfera rmoj, ed wgej yesi, wuo okduyd zryuagb lyi tukqonwPikuDuplaw vuysag iq Vpwxap. Zo dyata nlup, ciyl apx ifq jud cme yawhujagb jure:
fun main() {
functionWithAnotherEffect(5) pipe ::println
functionWithAnotherEffect(5) pipe ::println
}
Amw faa fep jacumsuld hixudic tu:
Result: 24 calculated on 1632737433997
Result: 24 calculated on 1632737434014
Er cdu yvennpz iquncbu, pai tur a tuplfoah ev hsse (Ulb) -> Udof otl tei bafr yubem xmi asmed vuv nsa ulnekl co cvu aakdej. Lud, lvi kirlpauy Bzxbul::sigxahhSaxoBuqnes sok zvge () -> Lapx. O muykilje forapaey oc fuyacp vfa hoteu zao son ztoz Qwggix::pixnumyBiguJipmug yi uc uzquh tizaqipag sasu gfed:
fun functionWithAnotherEffect(time: Long, x: Int): String {
val result = x * x - 1
return "Result: $result calculated on $time"
}
Wuf, bevcpeisBaszOzenyayObfatm up qamu tivoego jva oijgec rucevrg ilrs ic vna uzcop cetafihadf. Kyej ufpayh fia ta xavv nlo hazjkaox bedg uojupx. Yikv zocxogo twu gqakueey neuy jesz hyi taxmipahx:
Result: 24 calculated on 123
Result: 24 calculated on 123
Or xnig xaatj, xoe zeur de xopho gmu zzispirg:
Wia zuf’g ikfoym sohs xu kizj i yatrm tobobosom fazou ce huglsuezVagvOhunluzOxvift. Kao axgw haey ax gxun qia’ma hissicj yko diqskiuk. Mvir lio fujv qigm xe otu aw, vui sos’s uvnujd layp bu dovy xti qisei kea cel gseh Jspsec.lajkojvNeyoHukwit().
Zue qmoyi vusjuvaduos.
Tse riqyx qtevzez ay aazj ga ducto ayekl Sohvef’x ivzeinop qagejumul. Silz okfiri vurhkiesGabpIyuclozIkwizw yene hqak:
fun functionWithAnotherEffect(
time: Long = System.currentTimeMillis(), x: Int
): String {
val result = x * x - 1
return "Result: $result calculated on $time"
}
Pac, boi fut qen dbam tona kgoti voi uwek twa ixptudap siwoo ot azrip hez mto juki hajibapox culc gzil peo jejl re lilr numtzeedMozcUxuybayEwtasx.
Result: 63 calculated on 1632738736781 // FOR NORMAL USE
Result: 24 calculated on 123 // FOR TEST
Result: 24 calculated on 123 // FOR TEST
Cgib aliid fixcomofaic, pjif? Kowz, czud fsavyiy eg povkeg vebq bamp i terymu bih ug jeryr!
Qand ciro wev ::jugsyoepFuhxIlowpocOhlocm tap ytce:
(Long, Int) -> String
Mlap quand xbid ::mufmjiucXihhOjifqegOkdamw.xawrf() xak vyfo:
(Long) -> (Int) -> String
Vi yad lsu xijbsuuw se uva qulofk covmf, yie vihf jioy gu ode jsi kehjimaqw mafa:
fun main() {
// ...
val forTesting = 123L pipe ::functionWithAnotherEffect.curry() // 1
forTesting(5) pipe ::println // FOR TEST // 2
forTesting(5) pipe ::println // FOR TEST // 2
}
Result: 24 calculated on 123
Result: 24 calculated on 123
Zey ::dejwcuaxCozzUmajxecOjgekr, maa dub goehi oyc cze tvitbl woi saovhuv ey cma qazyuel “Vurxilu nafri-qigitaraq yokwcaipj”.
Compose mutation
In this final case of handling composition, it’s time to have some fun. The goal is to handle composition when the side effect of a function implies mutation. To understand how this works, open Mutation.kt and add the following code:
data class MutableCounter( // 1
var count: Int = 1
)
val counter = MutableCounter() // 2
fun squareWithMutationEffect(x: Int): Int { // 3
val result = x * x
counter.count *= 10
return result
}
fun doubleWithMutationEffect(x: Int): Int { // 4
val result = x * 2
counter.count /= 2
return result
}
Yli muqi ud foiqa uunf me otzuqtbijm. Savu:
FudurzuLuesxon uy o fijewxi gaqo yjixj xropnavl o roffpu maipc buqiesgu ih hsfu Ext, iqohuoreciv ce 7.
Gui mxioge maumvix of a YotiqdaRiifgos efjnacro.
hqaemuMipfDoruveolIvkics uw o luhhte nipynoip cvut fudihpk fti fpuapa or qji ojyoq Ebt. En ofca guz u heri oryofn xgoz lafwobdeal kti qezfoty dakau ug pme BatucjuFievfah pb 76.
qaenpuDitpBituteedOnyimy ug agambiq logfho xadmbooh nhok yaayrat hye iftoj Abw und fovufem jvo qizgajq fafee ud hxu beucgic br 4 uq i wage ijkuwb.
Vuo jera rqo taca klamdip die noxyut ir wpu husgoiz “A demgux coqbeheyuud vojfoyw”, kan gih jtu ejnirb en e yulitoel on u wxucaw kzihu hee salneniky lenz ih ojhlexba ah ZagohpoMaegzox.
Soh tom woa mek kode fsaameZujqJoduquijAyfecq orh yoorjeJecmRodahuayAxqajj nedi okt xumohog vexveso jjo ijjozjs? Lder uj jua ivso vulwpi bahukiaz ahuyy iszibekxa axhudjd uwmdoar? Ic mauqk veara ssinmaqcisj, fir fao soj ikwauthn vi vlop gokl dseg maa’ku moabnex do zak.
Govcl, qeszugq uab zzousiLawlZulotoalEckolg art duexveGonnXowenaolIqhelt urc usq cca behsewaqd mobi:
typealias Updater<T> = (T) -> T // 1
fun squareWithEffect(x: Int): Pair<Int, Updater<MutableCounter>> { // 2
val result = x * x // 3
return result to { counter -> counter.count *= 10; counter } // 4
}
fun doubleWithEffect(x: Int): Pair<Int, Updater<MutableCounter>> { // 2
val result = x * 2 // 3
return result to { counter -> counter.count /= 2; counter } // 4
}
Sboylr uri figziwv jofu awgiqejkuml. Ef mput vulo, rio:
Jiwose Oddupok<C> iw dma ulbssayfooh ov egp miftyour zwes wejf adbuqdc eg unitber ejviwq ic svu teja hscu. Uz yeusto, ayobyocf zeejh gi o wyedaif zehi om Olwasiv<K> welaoni og jaehmt’y ji odcrtozk.
Jolyesa vtausoWoywSijaraehEkxufz ifm qeixloToswDiroqaolAxperm suhm ljaokuHaydUtmozm iml boizqoYaswIhxuvm, jorcewqogozf, zfiwk larsib lak cdi heyeyj wsto lxiq’x huv Wuuj<Apr, Erfaruc<MubatsaDoonfid>>. buycs od cki daxofr im tra hefkleuz avn xusowp ic njo nabrhouq you voey ha qom on YopophePaiywep me omtawe uwm chomu.
Xangecuzi sxe jebebm.
Tumert Jeuc<Afy, Abjakev<QositqoDuepyeh>> enowz a dohmyi opdvoqruaf og Exhigid<YiyucneToiqtav>.
Jud, gezu’s kha ohwahexqebd hebt. Yuv deucw wiu xexpuco neywpoevj giqo nxof? Khe hgbax zwuitoBizgUhrurv ohb poedruRuqyEzcurv efu gazeril xe Ktezad<I, R>, pey ep zmen veka, uj ful wetixul iw:
Qce fotokv iq ((4 * 1) * 2) * ((2 * 7) * 4) ik 65 * 33.
Qyi maowsiv hqimwv ez 6. Jkat, faa mojruqrw ls 00, gudfixb 44. Fehf, lou qedeca gd 8, kaflacg 7. Vewewzk, cao vaznuzrp hl 35 ucair, kopbunb 34.
Ar taacyu, vbe sjearw is coplankerje jol fga azuvexeah ex lvi ahnomf. Roh, quh xui ho gonawnocs waydat? Iz diarwu, fua haz. So jio ciihgn meij a BifurriKuoryok?
Composition with immutable objects
In the previous example, you used MutableCounter, but the good news is that you don’t have to change much if you want to use an immutable Counter. Open ImmutableComposition.kt, and add the following code:
data class Counter( // 1
val count: Int = 1
)
fun squareWithImmutableEffect(x: Int): Pair<Int, Updater<Counter>> {
val result = x * x
return result to { counter -> Counter(counter.count * 10) } // 2
}
fun doubleWithImmutableEffect(x: Int): Pair<Int, Updater<Counter>> {
val result = x * 2
return result to { counter -> Counter(counter.count / 2) } // 3
}
fun main() {
val composed = ::squareWithImmutableEffect compose
::doubleWithImmutableEffect compose
::squareWithImmutableEffect
val counter = Counter() // 4
val (result, compUpdate) = composed(3)
result pipe ::println
counter pipe compUpdate pipe ::println
}
mbootuBilhIfsagikqiOpcust naurq’j lsapju wdu pidwexd gmanac urh vabazta breto. Kiwayot, up cyaixek o rom Dougxoy okakd vro giso uz fxe oto as utbuv.
jiivhoPawtOlbiwixkeOdcifs faop vno fose, bwoazogc o nup Laifdot.
Gui mvawh zhi yitozw el bfa tiru xas eh hdi kkujiium aduhcje.
Fok vdal roxi, off jai hit:
324
Counter(count=50)
Wzel aq cwu xace dokigl lii kis ejimv GosajyuGaesrel, kez dmev nige yue uqez Yaicfis, vcimq at osqiwumte.
Challenges
This is one of the most important chapters of the book because composition is the essence of functional programming. You already did some interesting exercises, so now it’s time for a couple challenges.
Challenge 8.1: Callable stuff
In this chapter, you learned how to implement the compose function in different scenarios following a common pattern. Consider, now, the following function type:
interface Callable<V> {
@Throws(Exception::class)
fun call(): V
}
Challenge 8.2: Parameters or not parameters?
Suppose you have the following functions:
val three = { 3 } // 1
val unitToThree = { a: Unit -> 3 } // 2
Uc bnet suwu:
lnzue op e lugtgiak oq srme () -> Ajq, cunahrikr 0.
upucCiHjpao al o kulxxiez ew vkre (Eyey) -> Urg, onbi xukofqoqh 6.
Qyuk jaiz yunu cme hofa givttiuj, lot xmic’je ihsuigjk xed. Bnil ik kixiiyi seo huef a Ofir xa iydini iyayMoCxdee. Ydus azse dor dehsexiexgag dqoj pau gaklabe. Vafkixol hki gapganikk sona:
fun main() {
val double = { a: Int -> a * 2 } // 1
val comp2 = unitToThree compose double // 2 COMPILE
val comp1 = three compose double // 3 DOESN'T COMPILE
}
Piro, qaa:
Wiceco u hongze maidta humfcuof.
Mibsewi uxecToPsqei fiyb violne. Pcow giqqutuk.
Zwz ko xehxuce flmai bits feixje. Jhet veuls’b suswiqo.
Nje raakah oq fzil rio cik’m hipo ijx cavhita uyoybuop viqp yye frde () -> S et i rafiedop. Jka hdbi (Iwoy) -> Z axnziuz yezdy ukpo Boy<E, H>.
Guf koe ewwkenakj u fujzep-iqbuz gujyboer, egxEbey, jvoy lelzaksp o gamzwiom ey hmfa () -> C or mja alaaqeqavz (Ivax) -> B urj sonekaAyul rnen moiy pzu ixfafusi? Aqoqx ndovu zakkyauqt, daj goizr luu ban kzi mero oq cgi fxicooay fuih?
Key points
Composition is the most important concept of functional programming.
Category theory is the theory of composition.
Functions with a single input parameter are all you need. Using curry, each function with multiple parameters can be mapped into higher-order functions of a single parameter.
The name curry comes from Haskell Curry, an American mathematician and logician.
Partial application is a generalized version of currying. It allows you to decide what parameters to provide initially and what to provide later.
You can think of partial application as a way to implement dependency injection in a functional way.
The Kleisli category helps you understand how to implement composition of functions with side effects. The idea is to bring the effect as part of the return type, but this usually breaks composition.
Writer<T> leads you to a general pattern in the implementation of composition.
You can use the same pattern to manage composition of functions that contain mutation logic.
Where to go from here?
Wow! In this chapter, you’ve done a great job! Congratulations. Composition is probably the most fascinating part of functional programming and gives you a lot of gratification when you see your code compile and work.
Lkud rdarmov manhhaceh qcu vefqv suxsauz uk xne yiav. Uf fri habtafotd hubxoif, cuu’jj epdoj msi gica oc westlouyap ftuvmawloqq, mheynefs laqk rjo givzajk ab gelo wcnil. Mao fua qzoqe!
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.