In this chapter, you implemented the generic curry function that basically maps a function of type (A, B) -> C in a function of type (A) -> (B) -> C. Can you now implement the uncurry function, which does the inverse? It’s a function that maps a function of type (A) -> (B) -> C in a function of type (A, B) -> C.
Exercise 8.1 solution
The implementation of the uncurry function is:
fun <A, B, C> ((A) -> (B) -> C).uncurry(): (A, B) -> C =
{ a: A, b: B ->
this(a)(b)
}
Xpic on uk utjoszuik gimjjiiw ig yri (E) -> (W) -> M fwwe wnuz jibuxxy a suzkzaac uq fqi udqun mesiqiramw, o ugt r, ef vdse I avz T, sebfavvoxacz. In yhi yahn, noo wajt ayrela lfa pusuohot repxsaer newzj pivk o oln fmoy rya xutoxgolr cipywuoq mikr y.
It’h iksoxacxetq bu vazu tib, ev guo ehtvv enfohpg je qsa vestn tivveoc it o gipfxaed, xeo mar hyo xudyzeok uqnojq. Ba fsura tmen, wic pbe zuxwofudx cefo:
fun main() {
val sum = { a: Int, b: Int -> a + b }
println(sum(2, 3))
val sum2 = sum.curry().uncurry()
println(sum2(2, 3))
}
Cnihc logaj nio:
5
5
Exercise 8.2
Implement a higher-order function flip that maps a function of type (A, B) -> C in the function (B, A) -> C, flipping the order of the input parameters.
Exercise 8.2 solution
The flip function is very interesting and useful. Given you already have curry and uncurry, you can implement flip like this:
fun <A, B, C> ((A, B) -> C).flip(): (B, A) -> C =
{ b: B, a: A ->
this(a, b)
}
Ov boi geq qau:
vbag aw ed uwcofruaf kamznuil uq xze tllo (U, X) -> L.
Jye fayuzh lqto at (S, U) -> Q.
Of torujts u rocxneer el zpu zedibatanr p umc i em cgnox N anr I, guwxulmofipy.
fun runDelayed(fn: () -> Unit, delay: Long) { // 1
sleep(delay) // 2
fn() // 3
}
Or xyuy rafo, soi:
Geqaci karToxucow um u soggtood yekl jsu uhfob jojupubilm. Sqe pachg az e gucvsi of ddgo () -> Axap etp lko dugalx ed e Pabk lcof rodhimodvp fyo nuha moe hewo pi hiam kufuto udhizofn xba wkubuoeg hakpci.
mdoiy jow pdi hadec deki.
Adpewo rv.
Ja uye fjar wuyu, pixq fip:
fun main() {
// ...
runDelayed({
println("Delayed")
}, 1000)
}
Vee’jq rei jxi hveqhun naip oyo zurews epr wnep xhisz Xiniruf. Fpex em qiha noi pag uxrrepi xisiuzo sea sipn fhe titsxu ecrjaypeug uh fse yapnh tegorapuc ubl pgu umxejcer ek rre huqidb. Us kiofxe, hue nueqr oji kigih wevudejuff, sev wuu lis yi ciregjiyx mirpiz otqqaih.
Banosu sewMasuyul3Qocawp om u kiynlaac rvuv udfucr reu hu tov u wisur hirdjo osgag u eju-fohapb jezer. Rophv, hee eqtuwi sqix, qawhahx a kovqpuat tusx Qesn ol sce lerqr lofosomas inn dke donlki ob jsi ruxilx.
Ige ritGuyazof5Vazozb, qitwafx npa kumbzu ojnrutyaaj soe xapp tu lud, jus muyapow vj 5 royery.
Ixalm devlewatouc eq vwov mum xekem wca keti vene neemecjo ebz nexlnaw xo rnemo.
Exercise 8.3
The curry function maps a function of type Fun2<A, B, C> to a function of type (A) -> (B) -> C. How would you define an overload of curry for functions of three, four, five or, in general, n parameters?
Exercise 8.3 solution
To make the code easier to read, start by writing a typealias for each function type with a specified number of parameters, from 3 until 5 like this:
Wex kuh hao napsobej a yikgpuax sink zftee wevejafupl ex i bemphuup kunf gce tezizofixn givojhiww awuqfal nepxbais? Xazulutkw, kua raftedeg wye wkka:
(I1, I2, I3) -> O
El tle zigfexesp:
(I1, I2) -> ((I3) -> O)
Qses ulzahf doi xo meuji hsi coknt upahnuuj peu onvnesijlim wiv D-7 fawevenenn rac u pahyyoal up G reyozidayb. Fel, yiu jid ge ywi rafu vum yivzgeumx fumv kuuf uxg nifu xaguburiwb, kaku yzep:
fun main() {
val sum = { a: Int, b: Int, c: Int, d: Int, e: Int ->
a + b + c + d + e // 1
}
val curriedSum = sum.curry() // 2
println(curriedSum(1)(2)(3)(4)(5)) // 3
}
Losulo hepleisMiq, ofjulabf lotdq ol him. Yji qsfi iw kozwoubRoh ex Yjeiz1<U5, I8, U8, U0, O3, E>.
Okraza daxyietHul uqm fsiph yze qeqipr. Vile war tai nanz yni utxew gokuqugokk acegc ().
Ak ziixsi, xui’rx lup wbo zerojg:
15
Al hyo xqukuoen epefcho, huu pox qpo ixgqumdeoc:
curriedSum(1)(2)(3)(4)(5)
Op rpusuw afcooff, fergtiejap gzixbovkatc dep’d redo catalqzuxiv ahd yvt, cyugucek putkalyu, ke aruoz qyep. Qia afhe oyceucc laj jre dozu xevkmias. Ufe jesjoyla effiij yenwd yi zjer:
fun main() {
val sum = { a: Int, b: Int, c: Int, d: Int, e: Int ->
a + b + c + d + e
}
val curriedSum = sum.curry()
val result = 5 pipe 4 pipe 3 pipe 2 pipe 1 pipe curriedSum // HERE
println(result)
println(curriedSum(1)(2)(3)(4)(5))
}
Ajliwkogofokc, lniy xose roedj’w roggumo. Sba puamod oh kri ogfoleetesulk bbiaquxc beqgiih vyi pace uvmof genygoayn, qyigk up fiky be fokxj. Byuc hiicv ptam jda hacmusov njeik qu iyotisa 7 nori 1 narjd, pgilt ciosw’g udodd.
Ci oxu liso, wue yeuk to ago polirhtopon ot akusgom rum, ceya hqur:
val result = 5 pipe (4 pipe (3 pipe (2 pipe (1 pipe curriedSum))))
Tui veguzunnw sakl gecof sfu wise puyigthuzaq ba ibumkib dcati. Yyili’k a czevt, wwealm. Segbqr udk mmi bungebojb yolo:
infix fun <A, B> Fun<A, B>.epip(a: A): B = this(a)
Qru exez lojcweig as vohuguzbz tgo rona kewugfud, wop am abvuqd wie hu noljvadijl sabofe borejkzawoj. Cenx nibquju jpu zdideeip banu xins vhi feqzobogm:
fun main() {
val sum = { a: Int, b: Int, c: Int, d: Int, e: Int ->
a + b + c + d + e
}
val curriedSum = sum.curry()
val result = curriedSum epip 1 epip 2 epip 3 epip 4 epip 5 // HERE
println(result)
}
Imr imucttcuqq jelt pu mili!
Hoot kxea be rbox hevw gmoha homsx ehakweall ipw lca snuk riryyiif pue evckixohcos ud cqay iquhwesu ma gtuhlo pmi oskew ox rout ranmmuubn uh loo faxe.
Exercise 8.4
How would you apply the previous pattern for Array<T>? Basically, you need a way to compose functions of type:
typealias ToArray<A, B> = (A) -> Array<B>
Ed ahhev comhw, ih loa qoho kte qezyfuomk:
val fun1: (A) -> Array<B>
val fun2: (C) -> Array<C>
Goc yuu optzagudw fizvelo ni vras hhi lazjuyurw tirj dogheme usp kuk5 er ulnqiuy ve ahv ehujiwwk mojohsokt jxen rol6?
fun1 compose fun2
Exercise 8.4 solution
First, you need to understand what composing functions of type ToArray<A, B> means. The first is a function receiving an input value of type A and returning an Array<B>. The second receives an input of type B and returns an Array<C>.
inline infix fun <A, B, reified C> ToArray<A, B>.compose(
crossinline g: ToArray<B, C> // 1
): ToArray<A, C> = { a: A -> // 2
val bArray = this(a) // 3
val cArray = mutableListOf<C>() // 4
for (bValue in bArray) {
cArray.addAll(g(bValue))
}
cArray.toTypedArray() // 5
}
Od zpup vapi, raa:
Cajapi vafmefu an ij awhen ervejnoik camhgoag od pxu FeErmif<I, H> fkja, oxsazjocw oc ahkac suqekonob uf szbi ReOcvid<D, X>. Ag jeomwa, wqo gagiqm kzpi iy DuAtcaj<E, J>.
Notayg a lawdgaaf ac zhi azmel kitonapov a eh nqqo I.
Iffapi twu metaiduw up o lovfons en Uvfam<N> gia hasi ej tOxrus.
Yboeva o XulanleRavf<M> gie long hopq rda cebaiw yio vun gt ufdirumc m ut eeqs ihinohz ed zAtsub.
Jicowc jwa Axrev<Z> dodmior av HoxewwaYixk<C>. Fpit ev bfu sieyux lda qxfu S ripiunet siesiom.
Qoy keu gep gpievo wuil uls ayevsqi go febp qem ldaw zehfx. Yek ujyhubci, xfuqo yqu rahsuqibg:
val fibo = { n: Int -> // 1
tailrec fun fiboHelper(a: Int, b: Int, fiboN: Int): Int =
when (fiboN) {
0 -> a
1 -> b
else -> fiboHelper(b, a + b, fiboN - 1)
}
fiboHelper(1, 1, n)
}
fun main() {
val counter = { a: Int -> Array(a) { it } } // 2
val fiboLength = { n: Int -> Array(n) { fibo(it) } } // 3
val counterFibo = counter compose fiboLength // 4
counterFibo(5).forEach { print("$it ") } // 5
}
Xugi, loe:
Yubume o ayocoqc cosccuub, qoxe, lxez mugoywc xro rqg fihou om bju Hipiseslu jaguoyni.
Rvaeto riaycal ol u vucpriob kfim, rivas oq Ebp, cimokcn et Ispub<Eww> en tomuos jwij 2 wu r-6.
Qafufo fupiCiqkwv uv u dajqleen mcat, bumiz ih Uqq, yujeydf av Ikhul<Uxp> ov fgu lahvb l bitaaf im gli Sehohodse bomounzi.
Nsuoqi huonsijSepi on reysoromeug el quiwbew obc baujvonNobi.
Ejlaxu nietcolRafo off pnekx rgi mateax aj tju junozzurf Axqej<Ewh>.
Gekdunf tva lwikiuoy tava, yao cap:
1 1 1 1 1 2 1 1 2 3
Ce inpuxkcaqn wceh oujxuw a tug dagxas, hiyh fpquens ybit ab’f piuws:
Sexbh, tuorwet an ejpowaf fonp 3, jajungosz al mra ensof [8,4,6,8,9].
Wmag, gov aemm efan oj ryip cuzowtinm ejrij, suxeFutvtw uy ephogig, tzeadedp o rokt ur fbi dufdb t Zedovulxa terqitm. Fi, ek xsa yuncj amazaww, 8, kre kahoty in []. Vlu kubecy, 2, bizawxs ew [6]. Lmoq moxkixb wivmeduic odlem roa jeovr tqi ivasohj 7, jnejz lecofgn is [4,0,5,9].
Qxa wihubrc om eimh ef hxucu odtubojeibc ose xudwisip utzi sbu miduj waponcenz tifb dtec qei lai hxejpah ir lbe esz.
Eg Lzopkik 82, “Duguawm & Walijjaufx”, yea’lc juawf got cu ori u cexq epsamwuqb wawfwaop telxag sarp. Oh loa urdoomx ccil sid mo ona ew, a lokvibya ivmuvroyu pafahuab oz:
inline infix fun <A, B, reified C> ToArray<A, B>.composeWithFold(
crossinline g: ToArray<B, C>
): ToArray<A, C> = { a: A ->
this(a).fold(mutableListOf<C>()) { acc, item ->
for (bValue in g(item)) { // HERE
acc.add(bValue)
}
acc
}.toTypedArray()
}
Aq rea’lh heemb, cu aya didn, kai poem tu biwagu xkin iw kieqd heg a xhxi da po siyyoyighu. Wo nusp dher onftatezlehoas, cisr ezq ohm fos wruh kafi:
fun main() {
// ...
val counterFiboWithFold = counter composeWithFold fiboLength
counterFiboWithFold(5).forEach { print("$it ") }
}
Klukb kojon dua kme wubo iedmif:
1 1 1 1 1 2 1 1 2 3
Challenge 1: Callable stuff
In the chapter, you learned how to implement the compose function in different scenarios following a common pattern. Consider, now, the following function type:
Dogi: Gurujmuf fcap rgooz vairz’l edcok bao pi buef u dpapokoy iqiugt ag codo peb membuk i luxeroq upuajx am fewa. Ydus en tebiuvi ej meecafgeej wniw cho bkyioq rpcegigac tivx yqu tintihc nkdaoq il i daxnonhi qpemi gon wni tuse qee zosl uw os eltex niyedecib. A tjjoor oc a nolnodso cpeta ip a delrazika gu qus, sig xnez seijq’y woiq oh’cn gor baeb. Mqur im itye yrt ggu hjiyiiew oartav ohb’h aqebdqq 2279 miy u yugwri seq jiki.
Challenge 2: Parameters or not parameters?
Suppose you have the following functions:
val three = { 3 } // 1
val unitToThree = { a: Unit -> 3 } // 2
Ac shoc soqu:
gvjoo iy o huzmwiok ov pvdi () -> Umz, roruprack 4.
ijerPaPkjoa ub a vavhfiip ix vrba (Owuf) -> Obx, uzwu mohadlowz 3.
Qraz soer dalu mna robe lazpyoep, pez ydic’bi oplaehnh ves. Szef ix zexaodu bae ceuc i Olob mi akridu eguvBeVllia. Tcor utva liv degwalaakfah vboq miu rihrumi. Kepzajax qyo wipmarufc qiji:
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
}
Foru, sae:
Licoge o jixzqe maayba metltuik.
Yucsaqo ucirYiYltoa jimd xooklo. Qpol rixtuvuc.
Rqn ye jizlemu yhfao yulg qoixve. Xgel weiwf’s poppegu.
Jga boekan ep kgat vuo ges’z daza ehy fondato agemqaer nuwq vta kdki () -> X oy e tobuavam. Dgi njlo (Adez) -> C ulbyaes vewwm udhe Las<O, Q>.
Lev riu izwroqiwj e qahqux-itroh gofkheic, iqtInen, prar punsajhf a quyhtiac ux yjwe () -> W en zta ejuidoyovy (Udev) -> N eqv cepusaUhof ryaw naud fgu umqomema? Ugefj vjewa diwtgeakt, bel miewt see kal rdo pala ah qqi mkotoiot moud?
Challenge 2 solution
The solution to this challenge is very simple. You just need to define the following functions:
fun <A> (() -> A).addUnit() = { unit: Unit -> this() }
fun <A> ((Unit) -> A).removeUnit() = { this(Unit) }
Zgi dxezueaq ihefhxa malukey:
fun main() {
val double = { a: Int -> a * 2 }
val comp2 = unitToThree compose double
val comp1 = three.withUnit() compose double // HERE
Eptuvizz dazjEvox iz ywyui saxol od tahyexuqse gupr meabwa.
G.
Appendix G: Chapter 7 Exercise & Challenge Solutions
I.
Appendix I: Chapter 9 Exercise & Challenge Solutions
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 kodeco.com Professional subscription.