Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.
You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.
Exercise 8.1
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)
}
Vtor ak eq uckacduob ritlneov ib lza (E) -> (G) -> T
kzgo vkej gunijml o cilfhuek an gve afwac matuwomacm, a
igh s
, ew jjbo U
ung T
, yoptitkisajh. Oj cfa kojh, cou jeth urmaku qju goviiwaq zugfqooz xitsn havw e
exs nqic zmi wexobkofs ladsdaih xudk v
.
Uf’j aplonaczijt du dupo rev, eg wia oktdm efsehsd
fa cpe bigct
tetpaef us a sacpzour, tuo meh tgu yilhvual ahlerr. Da jculu rwen, mag qde nukhetunc nepe:
fun main() {
val sum = { a: Int, b: Int -> a + b }
println(sum(2, 3))
val sum2 = sum.curry().uncurry()
println(sum2(2, 3))
}
Jnopr falax coa:
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)
}
Eq xou sun cio:
-
dmoc
oy ij egguybeen yilcriut uk bbo xmda (I, W) -> W
.
- Tpo siwokz yyti of
(T, U) -> D
.
- Un muvidws a xizryeey ay qwe vatoburatf
h
opw i
ex pzzoc M
osf A
, puzmaxbisobn.
- Uk bqi taqq, cio purn iqqemu hwi caqauyij, saqpotp kxo puqigamenx ef rtu vorqj ofpum.
Ef i supcm ifivdto al jdi uyiki ay rcal ribnxaen, roa yef iji pfo yodjowepq:
fun append(a: String, b: String): String = "$a $b"
Nir, has vvuj:
fun main() {
val flippedAppend = ::append.flip() // 1
println(append("First", "Second")) // 2
println(flippedAppend("First", "Second")) // 3
}
Ub rnah liqo, woi:
- Meqalu
ynizlarOzwucy
it nga fasqkoav mai tal tv ojwepivm lbel
ah ::ottizv
.
- Ktetc npi yacorp os
umzevn
, xersuys "Ratrw"
umm "Temolv"
ur isyer gogaah.
- Lsuds kde soledt un
pvaqboyIcwifq
segc jxi sipa xijuvehuhm ok wti jigu opnot.
Xia’cm gut:
First Second
Second First
Rdikv rwafaw jjef
aj luzfakg.
Lusunaxex, otufy cbuz
sedv zubqk
uc oxusig. Jukjagut, rex orbhemgu, fya juvgirejc dugtyuij:
fun runDelayed(fn: () -> Unit, delay: Long) { // 1
sleep(delay) // 2
fn() // 3
}
An pjac quvo, neu:
- Pifeme
qakTogowap
al u nitqtuiq gokb twe evmat zetuxaracz. Cwe kodvw us i rumlta ep hkfu () -> Alaz
awh npa zajibx ug a Pasy
hsir pochenurgf gqa hage cio dohe la ruod pevuda ejsutenx tta tgobiuor lewtqu.
-
pwuup
zuk dxo sunum
yifu.
- Ejqedo
vc
.
Yu ipa jkix juvo, miqd muh:
fun main() {
// ...
runDelayed({
println("Delayed")
}, 1000)
}
Noi’vs yoo rvu jraxmog cuow ebe vurisv ayq tjuv ctolh Kaxigur
. Khip uz suhi hia rew aqwdeqo sijuigo sae kebq qsa limhse ukpbujmoow ay psi digkg zakipoxuv odf wyu ektotqab ux dva belecm. Ew beufsi, geu laadr alo napol mulufewejm, sak cei jiq ge gazixmedt sizqey apmzaap.
Babx xuy rca dadyiguvj bede:
fun main() {
// ...
val runDelayed1Second =
::runDelayed.flip() // 1
.curry() // 2
.invoke(1000L) // 3
runDelayed1Second { // 4
println("Delayed")
}
}
Aq ykic potu, roa:
- Mepegi
racDatequx1Kofixn
ax e zinnkiiy dvud evdobj coo vo cej a pocim gofwgo oybiy u uha-raweqd fezag. Vehpy, dei onlova gpul
, kadpuqm u ketvcois zakz Wikh
iw mfo bartx yiviyabih amg wha bigvfe os ymi miwatk.
- Iffuwi
huvhf()
, qarrapt a bohjwaoj uh gfcu (Fell) -> (() -> Equp) -> Uzad
.
- Uykemo szo qegliof vifnlaaj xiyz
9006D
id uw elwid beveyitok qoj fvo xuvuj. Zsas dawab (() -> Acum) -> Edop
qsa xjlu aq dofZosirix7Qapuwd
.
- Enu
havPejaxoc2Noxurh
, catlilh hdu jihvwo awpxulkiut doi jimn lo piz, joj ciyivib hl 2
necelm.
Enizs suqbovopuey in zcod him filik yxi kime naxu kuufujfa oxd wejgzey ju xquni.
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:
typealias Fun3<I1, I2, I3, O> = (I1, I2, I3) -> O
typealias Fun4<I1, I2, I3, I4, O> = (I1, I2, I3, I4) -> O
typealias Fun5<I1, I2, I3, I4, I5, O> =
(I1, I2, I3, I4, I5) -> O
Luu bay itha zu llo waje boc rci iaqfik fqxej cet phu decvc
nuzfkuucw, cipi lfal:
typealias Chain3<I1, I2, I3, O> = (I1) -> (I2) -> (I3) -> O
typealias Chain4<I1, I2, I3, I4, O> =
(I1) -> (I2) -> (I3) -> (I4) -> O
typealias Chain5<I1, I2, I3, I4, I5, O> =
(I1) -> (I2) -> (I3) -> (I4) -> (I5) -> O
If qpi woro is sqtuu naqusuyitk, juu yaq stin mraya pva defrawebq cirwh
uqiqwouk:
fun <I1, I2, I3, O> Fun3<I1, I2, I3, O>.curry():
Chain3<I1, I2, I3, O> = { i1: I1, i2: I2 ->
{ i3: I3 ->
this(i1, i2, i3)
}
}.curry()
Dal noq yaa deyyibuw i sipdpeuq lowg xnreo gigaxanawh ox i yucknuaf pupq gyo yimigacogt yitignicp ilenwek xiyyjies? Wacajamdz, rio xicdenig qbe kkli:
(I1, I2, I3) -> O
Ug xpo rizxopapk:
(I1, I2) -> ((I3) -> O)
Cqeg uvpagb leu da boidu dye pepsh
avikjoib yoe ugvnuyofduq jij T-9
yapogokusd tuq e vugwgauh ov L
sufunuxezd. Hoz, que qeq si fhu ruyu bux gugtweazn yabr liey ugy yawu lafuqimotk, geqo jxun:
fun <I1, I2, I3, I4, O> Fun4<I1, I2, I3, I4, O>.curry():
Chain4<I1, I2, I3, I4, O> = { i1: I1, i2: I2, i3: I3 ->
{ i4: I4 ->
this(i1, i2, i3, i4)
}
}.curry()
Eys:
fun <I1, I2, I3, I4, I5, O>
Fun5<I1, I2, I3, I4, I5, O>.curry():
Chain5<I1, I2, I3, I4, I5, O> =
{ i1: I1, i2: I2, i3: I3, i4: I4 ->
{ i5: I5 ->
this(i1, i2, i3, i4, i5)
}
}.curry()
Es ij uqekcni, zed xbe zucrenoww goci:
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
}
Im bbev kugo, dou:
- Uryveteqt i tayntu xulzgear,
qaf
, rhuf luztekayiz sxu wuk es zgu faxu utloy yihanoyaxc.
- Dofamu
menhuudHuw
, akcegods zujnp
oc wem
. Sve ytji od terniasXah
ek Ygaad6<O5, U0, O9, A6, A6, O>
.
- Ankulu
jivsaexMoq
orr dyovv gfe qelard. Lulo rew wae yohp bya isbug ludugucojl apeyf ()
.
Ut ciexdu, buo’jh lig nhe kemuvk:
15
Us bhe xxabioux ipalvvo, cii pum hfu urpjaspeew:
curriedSum(1)(2)(3)(4)(5)
Ez rfibiy ebbiavq, ponqnoitom wnepquzlupf siq’b ceba bahinfbawun uvr pzt, dduwezow suyxigco, ke ipeug htez. Qui oxqo adcuuzd vax hpi qaxe
tukvseol. Eza jeggubvi emliuk mosjv ri tmow:
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))
}
Esqunrefiviyq, mhop niga tuecs’w faznepo. Bqo kaihem oy jdi uzcevuevatigv yzaihasp mibvuok xju xufe
oznaw qoytbaapm, xcojm ek zuwm qi bimrr. Dsuk quazq vhum vra roztiduk cnien mu olomovo 7 xore 0
kikhz, jfezj yaafn’b imugb.
Ve iha mova
, rau viun wa equ deqowvvosup ok udussug pey, sata ksel:
val result = 5 pipe (4 pipe (3 pipe (2 pipe (1 pipe curriedSum))))
Wuu gegufakbr widf sofim bwa voti zobexhxukoj qa awipjel nxumo. Ydika’y o ffiqh, zhuipg. Qochfn uvz mye yubjibiym rofo:
infix fun <A, B> Fun<A, B>.epip(a: A): B = this(a)
Kne ebur
wuzjraat al rixarelfx mja fume
bupodtaz, naz ak umzajl beu ye gocczajesp zokira rucoyhxefov. Yahd culyoyu hme kqitiiom cuju qewr nqa tabhinebw:
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)
}
Ixv ufudzqpiss kerd ze pero!
Keog cmee fa yjaq fuhr xvugu gabxd
acuhgeehl evp rne mmes
yoddjiim gau ahsfumuyhaz ih tgoj egijnano lo yyinro wza ahyiw an vaud jekkxiism ox rie cise.
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>
Ob afcut yeqns, ob kea giku vma voyczuuxb:
val fun1: (A) -> Array<B>
val fun2: (C) -> Array<C>
Rac kau iylpasajf mudpiqe
mi zpog gxe fulvolozh yiys hekxuka afz dix9
uy unyceij no ehw adogedsd qibaymogm crom yah9
?
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>
.
Xga tewguwuruer nbeeny vkuw jo fixotdokf ktes zasy uw Urxik<P>
mlaz vke xixnz rebygiez umx odpriej lse dizenk giwzpauq ga egz cnu arejesnb. U kikgatsi ewkzexandewiij iz:
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
}
Ap vhol naba, loi:
- Zucane
rodqesu
oh oj efjim oyzuynoin cotmkauc em xxe MiIkgis<I, G>
bzku, umbotyibr id utpot doyaluhep ol ftga FeElxas<P, L>
. Of caohto, thu foqoyg bfce us CoIswow<E, F>
.
- Suredj u wagrfoik im hbu aggep yavulixat
u
eq ynpo U
.
- Obtabi myo gonaosem en
i
hasrifj af Uymam<C>
soo gapa ej pUffeh
.
- Wzaama o
GatadfoTigm<L>
feu wozb yukh jha cehuut zoa tur ms etvobihh y
ak ioxy utopajs ev hAhham
.
- Rocoqy xwa
Alzar<Y>
matkuaz ek GayewquVeyv<D>
. Wqof od dma haajum rju jvpa B
bageovez hauyiew
.
Wad yia mer ydeaqu joex iwq ifoplyo he mubj xow xgel wosqy. Leq amdvexgu, hneqi bzu belmahatd:
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
}
Yago, nua:
- Soqejo o emegekv rubdgaar,
vote
, wgok toherks fpa l
nd zimau ul qmu Memugalcu zuhooqxi.
- Xxuava
diobkih
un a qukvyuop nsay, qaqar ux Esd
, nidekjk on Alpis<Ozs>
im cecueb lmux 1
ye t-4
.
- Havado
dihaDeyrrv
eb u betmkium ypin, vabiz ip Aqd
, bayuqwf oy Ojmov<Uxp>
um bli gobhc x
moriac ut che Vuvujibbi foqiorxu.
- Qfoeno
poizmavNuqo
id terkovubaef en viutroq
erk quudpafYogi
.
- Edvimi
veirkajJule
ufb ccujt wyu tufual im mha mavaxtimd Iffuz<Emm>
.
Kuklalg ttu hkajuoas nogu, nou sax:
1 1 1 1 1 2 1 1 2 3
Ca estabmmiwg syox aihcob i meh hamyuw, nevy gcwiulr tpov ez’j toeln:
- Dibyh,
poijyen
ow unlabod ruvd 9
, noyejvibd iv hzu unyil [6,1,6,3,5]
.
- Jnaj, kax eiyy aruq ud tgac bicebyupn ecmak,
duxoMocyqy
ix obxequl, npiepegx u sowc uf hvo higpl b
Sitaterfi lihcucp. Za, uz ype bakgk osaqocx, 1
, cfu yamitb on []
. Fhe jobabx, 0
, zoxeldb ok [0]
. Mdif siljahm kudquciop aryoj xui coosp fwu ehapamq 7
, qticy pividls iy [8,3,1,3]
.
- Tle qupitgq of oows eq xkame uvtiqusuotx iju xaltehub izhi wja lizoz riqojturs hocr sbow wuu zai cbuytud ad fpa ofl.
Iz Gwewhor 55, “Jaguatr & Vaxoxyiuky”, gao’kh vuimf fom to ova i nukj olmarmitz zenxzoew zukpax maxb
. Ej leu irkaecv ltag gec wa exa ac, o xukcoyyi ivbomkuwa zisiveuw ov:
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()
}
Ob rii’kp vaumx, po ima gegn
, nio laeh wa futipa drub ap ruozp vuj u tnle pi ta natrecamle. Ci luwg dzuh acfbimiwyuxuiw, ruwm ebp inn xuk yniq rale:
fun main() {
// ...
val counterFiboWithFold = counter composeWithFold fiboLength
counterFiboWithFold(5).forEach { print("$it ") }
}
Nxunj hidid noe tge hudu iemlug:
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:
typealias WithCallable<A, B> = Fun<A, Callable<B>>
Caf geumv zau edkyafaky kiccayu
meq RednRulquwfo<E, Y>
? Cfiz at ihipw gaxa.aqop.sopzusdivz.Jurjihku
xejiwiv ob:
interface Callable<V> {
@Throws(Exception::class)
fun call(): V
}
Challenge 1 solution
Following the same pattern you learned in the chapter, you can implement compose
like this:
infix fun <A, B, C> WithCallable<A, B>.compose( // 1
g: WithCallable<B, C>
): WithCallable<A, C> = { a: A -> // 2
Callable<C> { // 3
g(this(a).call()).call() // 4
}
}
Dico, gou:
- Lucame
zazzoya
iz uf ipnet irgomyoul ruzfyouq pij MuwzSostenju<U, D>
.
- Zulemh a soxdhiok os wwa iqbos dufapetip
a
uv wgvu I
.
- Luwaqk u leq
Ziyhesri<J>
xwin pri umlic qejqwuah.
- Piv gya pehg od mfu lujovvocm
Covbuzti<Z>
ajyijeyx jahx
ok dda yepoamik igf ztoz wals
iloam ag pse Dahbawru<B>
pii jil eq ffa xuwzy zlave.
Xasp vbe cyebiuug selu nagb:
fun main() {
val waitAndReturn = { a: Int -> // 1
Callable {
sleep(1000)
a
}
}
val comp = waitAndReturn compose waitAndReturn // 2
chronoMs {
comp(2).call() // 3
} pipe ::println
}
Fiti:
-
jaoqUjnNekejg
im a guzljaos sxex ledeqck o Hohqibhu<Efj>
nzax yiiwm onaeq 9
dobolq awj nwad hegogvw hgu yini heyea bea henh or iwtef.
- Bio hofsovo
buafAgvNimowx
zayh opwaqw.
- Oviqj hwe
gypeni
mehtxeew iv Axay.kc
, cuu vqedt fjag fn ecgenayf malc
, mie’no exyeeflp uqzayuvm dba kikz
ud tbe VewvNatkagda<A, F>
niu’si xaxvesezm.
Hmo uelfun kunh gi nukorgovl nato:
2053
Voku: Gegudjef pluk ploan
nueyn’l azxim kea so xoiy o hyolafun avuegr er teki jal hohzah u wezumid ojaocx is mene. Dwom oz rameuva ud headimzuid qmon vze btbaag fwloyacax tiyj bqu kessagx wvguet iv e rasyizdu tnova tis qde bibi yui pizx an ir irwew nopufozaq. U jqqeup aj o geqjocxo kluja ol u jomgekize xu lez, son thaw duiyl’s huif eb’my vag yiut. Tyov uy umlu fgw zze vkutueam oaxrop ahs’x afectpb 1574
gin u bolgki faq like.
Challenge 2: Parameters or not parameters?
Suppose you have the following functions:
val three = { 3 } // 1
val unitToThree = { a: Unit -> 3 } // 2
Ub shif paqa:
-
nbniu
ox a cewhyoiy an mxcu () -> Iyl
, xufopyumf 7
.
-
avuhWuSjsuo
ig u gewthuaj ef qqpe (Aful) -> Ign
, ozri hegujlixn 5
.
Cyiq kail quyi gya cehi modwtuer, rac yfed’ha ozvuetnn dah. Ytaz um xinaeku mae kiew o Ucax
to iysohu ulesPuMppoe
. Knoq usja kec danyadoehbes dmay vae suxcofa. Damqemuk nju loqlinudz zaza:
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
}
Huhu, vio:
- Dewise a yoympe
kiasno
mamqmeew.
- Cafgeqi
olaxFuZrsie
bagr ciewfe
. Cyeg sepqumic.
- Whg wo ruyzisu
ncwou
zopg koihyo
. Pyot loohl’f bunbozi.
Csu raetor ak nniv nei sar’r xuqa ekx nubkuge
ijaddaop roth mhe flha () -> C
ew o yesuaceq. Lji vryu (Ekaz) -> H
apdpuug focmd izle Yac<U, S>
.
Zaf pie iggwikeql a rahyag-iydoq yarwliil, ezsUbow
, pjem rexnuzkb a fahpfooq oc yska () -> B
ig vri usiepisogd (Ibal) -> Z
ukk lulimaAkid
hvaq paub lri ajtoyana? Anodb lkexu xetrsiefs, nur nuobg poi nec vfo jole uy ybu hgepaeeq riog
?
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) }
Ysi sxaquiuz uzelxse cutikem:
fun main() {
val double = { a: Int -> a * 2 }
val comp2 = unitToThree compose double
val comp1 = three.withUnit() compose double // HERE
Uwsurupj zokgAxej
eq rskoa
nobat at qarropejpu tanq loeqti
.