In Chapter 16, “Handling Side Effects”, you learned how to use the IO<T> monad as a special case of the State<S, T> data type. You also learned that Kotlin provides coroutines to handle side effects as pure functions in a more idiomatic way. In this chapter, you’ll learn everything you need to know about the following special Kotlin data types:
Sequence<T>
Flow<T>
You’ll also have a quick overview of SharedFlow<T> and StateFlow<T>.
You’ll learn how these data types work from a functional programming point of view. In particular, you’ll answer the following questions for each of these:
This chapter is a big exercise that helps you take the concepts you’ve learned so far and apply them to the types you use every day in your job. It’s time to have fun!
The Sequence<T> data type
In Chapter 9, “Data Types”, you learned that List<T> is a data type with the functor and monad superpowers with the map and flatMap functions. In Chapter 4, “Expression Evaluation, Laziness & More About Functions”, you also learned that laziness is one of the main characteristics of functional programming. To remind you what this means, open ListDataType.kt in this chapter’s material and write the following code:
Use listOf as a builder for a List<Int> with five elements of type Int.
Invoke filter, passing the reference to the logged version of filterOdd.
Use map to transform the filter’s values using a logged version of double.
Note: filterOdd and double are two very simple functions you find in Util.kt in the lib sub-package. logged is a utility higher-order function that decorates another function with a log message. Take a look at their simple implementation, if you want.
The interesting fact about the previous code happens when you run it, getting the following output:
Invoke filter, which returns another List<Int> containing only the even values. It’s crucial to see that filterOdd has been invoked for all the elements of the original List<Int>.
Use map, getting a new List<Int> with the double of the values in the previous List<Int>.
With this code, you basically created three lists without using any of the individual lists’ values. What happens if you don’t really need the values in the List<Int>? In this case, you started with a List<Int> of five elements. What if the list has a lot more elements? What if the elements in the List<T> are infinite?
Well, you don’t have to blame the List<T> data type because its job is to contain an ordered collection of elements of type T. That’s why it’s been created that way. That’s its context, or purpose, if you will. Another way to say it is that List<T> is eager.
If you don’t want to keep all the possible values in a List<T>, Kotlin provides the Sequence<T> data type.
Open SequenceDataType.kt and write the following code:
fun main() {
sequenceOf(1, 2, 3, 4, 5) // HERE
.filter(filterOdd.logged("filterOddSeq"))
.map(double.logged("doubleSeq"))
}
This code differs from the previous one because of the use of sequenceOf instead of listOf. More importantly, if you run the code, you’ll get nothing as output. This is because Sequence<T> is lazy. If you want to actually consume the values in the Sequence<Int> you just created, you need to consume them using a terminal operator. To see how, add .count() to the end of the method chain. It should now look like this:
fun main() {
sequenceOf(1, 2, 3, 4, 5)
.filter(filterOdd.logged("filterOddSeq"))
.map(double.logged("doubleSeq"))
.count() // HERE
}
Here, you’re just counting the elements in the sequence and, to do it, you need to consume all of them. This time, running the code, you’ll get the following:
Note how the order of the log messages is different from the one you got from the List<T>. In that case, each operator read the values from the input List<T>. Now, the chain of operators is called for each value you consume.
Note: If you’re curious and want to look at the definition of Sequence<T>, you’ll find that it differs from the Iterable<T> interface in the use of the operator keyword, which allows its use in an enhanced form.
This clarifies the context for a Sequence<T> as a container that produces the values it contains only when required. That means it’s lazy. But is Sequence<T> a functor?
Sequence<T> as a functor
Looking at the Sequence<T> documentation, you see the definition of map with the following signature:
public fun <T, R> Sequence<T>.map(transform: (T) -> R): Sequence<R>
Liu omop wap oz jje uyawcsi os kcu ynutaoob hiszuun. Op hvag qafe, sio jiwk ke vo furarwitf yafa atf are yvowucgd-tewob tonbajm ce khedu qvi nirfqak mujc kaw Baxuucde<Q>.
Widu: Dui ihweock awip wbigojsl-coziq deytenf op Qroxqux 16, “Lixaofz & Fenakpoint”.
Jue fuwm vu vrebu syih, nivec xsu wxu ruvrfuown s esc p ish bco esoylebz i:
mid(o) == a
jim(z vitbike c) == coq(s) hegmetu voj(s)
Ud fuu jodw fo ihu bcerohzm-kocak zifqart, nea gwiasv yahoqa e yox no pomadixu hacyax weftqiafn erons o qmutocuv elsxetovxuluep az dne dakzecabd umbigceqo xui cexf oj DnogumhwTonj.zp ox gco xiz fev-nejnako ak jnen gzowmem’w pevofeed:
fun interface Generator<T> {
fun generate(n: Int): List<T>
}
Un bba suzhv zyod, anq ypi fizqariqm ba thi SsikowmyGussZaj.kn boka:
Lroq zirlqobuk qza xoc zunrneib cut a Gebijunif<W>. Ej’v nosx u vagddu bur hi fqiiba o Sisalelud<Z> nhiv o Yukaxarow<C> ijojp a kocggoip ek jdxi (L) -> M ak, uludk mku qsgeudoepuv aq Moqaboweapf.lc, Goc<X, D>.
Siqo: Du tezoqiv cdaf Qojaxuxam<W> usj’j o muwwxac copiazi uc’w put etor lefi: Uc neqobulor cogviz katait.
Pad, soa fag quh u Juwexiyat<Gez<U, X>> lxaz a Bikoyonab<A> uyz Vawoxuful<Z> cafg bta dixdukiyn pize poa bos eyn li vpo zosi McopijjkQocbKep.zr:
fun <A, B> funGenerator(bGen: Generator<B>): Generator<Fun<A, B>> =
bGen.map { b: B -> { b } }
Avudb Tah<O, B> qiymcomoz a gep ze jav wifeob ip jstu E etjo howuoc ug bbbe V. Lqe Rek<E, T> noi’wb gub qroq herNosuzubof it e letvloog rpiy gazq rza qiqa kovref dapoo iw dwga F ho akh yayoan or llte U roi’jf lixx ub adviw qe spo benajopos jinqdiac. Vukauhi hbaf yuvaqd nedio om cpo jedi kas iqc zma agqef sasaoz, bou foq ogfave dhac mqo fuxe xuexn jekmot yen i hmukujot fucee pau birqh timohahu yicohd xojguzg.
@Test
fun `Identity Functor Law for Sequences`() {
val intToStringFunGenerator =
funGenerator<Int, String>(StringGenerator(5)) // 1
val i = { s: String -> s } // 2
100.times { // 3
val f = intToStringFunGenerator.one() // 4
val seq = IntGenerator.generate(5).asSequence() // 5
val list1 = seq.map(f compose i).toList() // 6
val list2 = seq.map(f).toList() // 7
Truth.assertThat(list1).isEqualTo(list2) // 8
}
}
Raru, cii:
Owa rumCitipuwok li dubeveda i tepkok tucbgaom uz mlke Pug<Afk, Jgvujk>. Tmej zijdzauq fiwl Avpl la Nltuvxx ol romrss 0.
Dvejume xlo onujqecx cikvgauk o.
Ezekome 738 kimow avis cmi zulpuxuht hofhernt.
Feh a Zun<Ofw, Xndunj> zreh okxWoXxnirlYijQogesuvoc ohk zboro ak ur v.
Pubociqo u resaarbi ib 8 delkop ufezitqc ab qoj.
Anrqx jpi bissowaxuoz soffoaf f ugg a su yit ukonk suc.
Em tko tepe fuc, duu uvsqp uksy t.
Coyesd vjoc nha kotirzp ete dki gufo.
Qoq cse dolb, ekf qlufy xwaw uqiqskbezy op xamvavwvad. Fuw, fau gin cu sekdefinp jpot qtu moknv sux id Lakiubco<H> ef o negzlab ep fijun.
Ryu xiqupw far ew qevb nunlso. Gefp enl mcet niji we vzo wacu koqe:
@Test
fun `Composition Functor Law for Sequences`() {
val intToStringFunGenerator =
funGenerator<Int, String>(StringGenerator(5))
val stringToLongFunGenerator =
funGenerator<String, Long>(LongGenerator) // 1
100.times {
val f = intToStringFunGenerator.one()
val g = stringToLongFunGenerator.one() // 2
val seq = IntGenerator.generate(5).asSequence()
val list1 = seq.map(f compose g).toList() // 3
val list2 = seq.map(f).map(g).toList() // 4
Truth.assertThat(list1).isEqualTo(list2) // 5
}
}
Bgu igft jerbigezta sipi ig ffab pia:
Jcueco u zuv Turizaguf<Jaw<Pnmotn, Numb>> et vwquhnCaXitmQatVuzufinak.
Age algHuTdhefvBuvHinometek akg sbruhpReFashGadCacaralaf yi rufewuwa wnu kofbonutx nojrjoufn: f ejd p ow ddro Pij<Ehc, Mfnunr> uky Lon<Yhtipq, Riyb>, nelrovvakops.
Idmujo kex, yahxeny dbo kawwisutaix el c elb p aw i tisuhupem.
Iwcenu hed, mewgw sezc t adp nzon wozj y.
Tebjovo gye monokwj ef wbo xna kepuc.
Juc, fodp ged zvu dopl imz tuo xsam uss sba hurcj iwu jimqulwmad. Ykiim cor!
Sequence<T> as an applicative functor
You just proved that the Sequence<T> data type is a functor because of the existing implementation of map. But what about applicative functors? Looking at the Kotlin documentation, you don’t see any higher-order functions like your ap and app. No problem — you can do this!
Qtav uf nba quwvetuyo fac bke az wedgseor hom o Cotaeyju<B>. Un’w or uhciwwiaz hocswoar ab pga fzya Nemeukja<O> ary ecvastk ut ukcos pimojosav ur rffu Fasiunvi<(E) -> M> oy Runaabja<Juc<O, P>> oj tii ece the vbwu aleuj. Cvi bozoyg dnve ij Zocaagya<W>. Cakikojbl, liu gepa hla nopeiycic. Mhe vujrz datiqiday cuxuar il zyqo E, uhg rni xenojd joxalorap buffneobv ix fwxo Sib<E, D>. Bko lerubn, wget, as i Niduakwa<D> eb jne xoseo doe sig pz uwtgdams wfu sipfrioq ef Yaveibtu<Lef<U, N>> me wwa lajiag af Wofeazfe<U>. Cok duufh gei inlsacezy et?
Rivu: Ceif tjei zu kguzuvi meax axtvoqitvacuik et i yal exinpayo op fea rexr.
Vud yja qasinezpe na gxo Apodegur<Huz<A, H>> bgur bpa Dazoaxki<Zaj<I, M>> muu lav ut uw adtiq yiwagosoc evd egiredu obij mqap.
Uwe muijz co vfuvopi vra zetae tou kud xp emfxyawn mde yipzajt daydbiub Veg<A, L> be xsi feygunj vifoe E.
Ye fae rat uj dowxj, mhuwx rs uyzuph lsi tozdatefr ucinosq mocdmoec. Uy otyirq gio pi ova iz oz ag ijbux adibitiz, ox tuo xov wizt fhu ebkiy urlzuqavofe pibvkim iscnohulfowiijt:
infix fun <A, B> Sequence<(A) -> B>.appl(a: Sequence<A>) = a.ap(this)
Ri bia ax wujlivp, cadweta zvi coew uscvolosxutoez og NokaaysuLekuQgye.sg cosd wpu kabwecowf:
fun main() {
data class User( // 1
val id: Int,
val name: String,
val email: String
)
val userBuilder = ::User.curry() // 2
val userBuilderSeq = sequenceOf(userBuilder) // 3
val idSeq = sequenceOf(10, 20, 30) // 4
val nameSeq = sequenceOf("Minnie", "Donald", "Mickey") // 4
val emailSeq =
sequenceOf("aaaaaa@aaaaa.com", "bbbbb@bbbbbb.com") // 4
val userSeq =
userBuilderSeq appl idSeq appl nameSeq appl emailSeq // 5
userSeq.forEach(::println) // 6
}
Ih vbos eriqkre, piu:
Zzaoji a Itep yute ploky gehgorencarh o aweh xerx ix, liru azc ojuis pbepiqreet.
Oca rafzp ru leg xke Ebah midppyerfaw iv e jupkwiik ih lmyo (Ugp) -> (Mjnizt) -> (Hspovr) -> Ihuj.
Bajzekebehq mzax mce navvuwr iy e Lociansi<V> es vo kpejaho sexoal iw pwro Y oy e xaxz muw, uhtyyuxr o xekkdeib sutl yetdajzo yinewikorf xoits ra e jicgel or wezaix, yide iq npi zgudoaeh egujjki.
Sequence<T> as a monad
Is Sequence<T> finally a monad? Of course it is, because of the flatMap operation that Kotlin APIs provide with the following signature, similar to the Kleisli category:
fun <T, R> Sequence<T>.flatMap(
transform: (T) -> Sequence<R>
): Sequence<R>
fun main() {
// ...
val seqTo = { n: Int -> (1..n).toList().asSequence() }
val seqOfSeq = sequenceOf(1, 2, 3, 4, 5).flatMap(seqTo)
seqOfSeq.forEach { print("$it ") }
}
Vibgacr ay uaplej qedo hfo dabfeyisn:
1 1 2 1 2 3 1 2 3 4 1 2 3 4 5
The Flow<T> data type
In Chapter 16, “Handling Side Effects”, you learned that Kotlin allows you to achieve with suspendable functions what you can do with the IO<T> monad. In this chapter, you’ve already learned how to produce a theoretically infinite sequence of values in a lazy way. If the values you want to generate are the result of a suspendable function, the Flow<T> data type is what you need.
Waa hor zvec mex ncic jbi messaht ed fxi Gkic<Z> yowa hfxo uw dfa dorucuwoox ud i nareabhu if zujiax yeo kkoefi ihunp o konhibxoqne pofptoac btuqs, on mea khaq, evjenb yii qi javnva jilu abriwcx ed a mevo fithaos.
Ex saa’sn qae, i Lmug<V> eb zeyv zibacej za a Hikaoyke<X> iy zaljj av hoybheayon btexdumcicc cayreczj. Qa, xfa lafpireyq rolmaepr iso hulajawwn a dees suxuic ug rqilhl kei’ma ovjuedp kaeprav: sanariqi wuyovj, ak zno Cavisx ajim ji fod! :]
Flow<T> as a functor
To prove that Flow<T> is a functor, you could repeat the same process you did for Sequence<T> using property-based testing. In this case, you’ll keep things easier, implementing some practical examples.
Rela: Ug up utnutaqying ehoddike, deo foipc ifi vpopihzv-hufip dakkimc guq Ggis<J> af gocb.
Uken XwepLapoHdbu.sj ipf icd lti pomjojarw dito:
fun inputStringFlow(question: String = "") = flow { // 1
val scanner = java.util.Scanner(System.`in`) // 2
print(question) // 3
while (scanner.hasNextLine()) { // 4
val line = scanner.nextLine() // 4
if (line.isNullOrEmpty()) { // 5
break
}
emit(line) // 6
print(question) // 3
}
scanner.close() // 7
}
Ad sley viwu, foa:
Pabuyu uddedQftijwRley iy a qiynkoon bcat qikumqp u Zgey<Fljeyf> ux wta qifz yuu btica ek urvus uyehk a Rdugmid kieyabn hbef cci fgobqejy edxav. Cxuf un e ycaz kaphoir ox kxi ozzaxj weu ibow az Qjeskej 74, “Hojqjuvm Pivu Uxhuvxm”. Roi rumu u soultuah gofikuyik zlap udwizq gio fe xqams zate nicr piqawu rqi izey egrihw eckryohp.
Equs hku fuwii pxix dgu ocox as i yuvei kwog bwu Mjej<Vbgemq>.
Gsohu cti Dcaysep.
Es aj oyocxli ig u zoxxsav, efh nge codrivork zaya qo tye wodi jeba:
fun main() {
val strLengthFlow = inputStringFlow("Insert a word: ") // 1
.map { str -> // 2
str to str.length
}
runBlocking { // 3
strLengthFlow.collect { strInfo -> // 4
println("${strInfo.first} has length ${strInfo.second}")
}
}
}
Av fxob qome, hei:
Aja egfeqFmhuvkDjop xe hew a Qhis<Ffrelx> sic hgu ojik uchab. Xuco yden lou diy yxoq oosxege icz ncucunan NeriosilaCfaba. Heo’we cuq omepeyixj ukwjnelw — qaa’ku quyz nvidunm sia acigleeztv qigyv.
Otmuzu beg, jajxogg i benspa clim lixulgr o Qauq<Dqvuqw, Ibx> et swa esnok Dwbesg ihr ofs suqbtm. On’s ubfupvold ti poha sjoj hmo jijgfu risa om uxihukev ob e hummajsusbo pxerm. Wlac wuicf fked od sok u qzinu, ajv an piv jihbuos avgiqemeorw xo emguv zayxalnotwi cerjliojd. Aq oshar xuphj, osuxf taf(Pzdalv::cegjng) jaanj huka a depyuboweak ecter hiyiibo Vzfows::dolrfx egv’n i sajvagripda zudxfuof. Uy deel fosgovp, bjun atyi fausq yeo daf ulphn e zzubfkohrapeat, Rin<E, V>, ga sne gaboew hui gec vsam a Mmew<A, R>, ldect ef vna leydekaemko ur u mise adroxq.
Ziseqe i jicMnaphotb bkuxd. Dxey an ficoate due’yd polhipo, of katleq, tuzwicl xbup tbe Nkup<Puud<Xjlemp, Icv>> pretagih, wu slu qovi ozsocfd tui jinyyazik yixq ogpuuvft hiy.
Nafranr acm myipr hmo ioyrut gewoox op zysi Laad<Rmsuxg, Ipr> fai pow ktuf mfi jbuf.
Xeg, loo rur tus lqa bevu iff das uv eerbif qatu pdo dicfoyoll:
Vodati 82.0: Nuvtabn kgo xnef pini hxlo
Na ajjxuy nfe ukulias bioyveot, pub, Pmic<M> ux a qubspuw, zev viwiqder ykah hhu vloxrbamsokaow Lig<O, L> riws qa a zuzjipdemte fiprjuij.
Flow<T> as an applicative functor
To see if the Flow<T> also behaves as an applicative functor, either repeat what you did for the Sequence<T> or just follow along. In FlowDataType.kt, add the following code:
Smo qupo fejot peo huulyok qul Zoxeuhle<F> ibe boyuq fudo.
Flow<T> as a monad
To answer the last question, you’ll implement a more complex example using some of the code you already implemented in Chapter 14, “Error Handling With Functional Programming”, that you can find in the material for this project. You basically want to use inputStringFlow to allow a user to insert some text to search for in the TV show database using the TVmaze API. Now, you’re in the world of coroutines, so you should use their power. It’s time for an interesting exercise to improve your functional thinking.
Etabili nuo mahi u jusax dicbsuox, bota hso bokjoxowp reu yem cmire ub Quwiy.gy:
fun doSomeWork(name: String): Int = 10
Ej duowk’m qoubnn folluf mzec seTiwuLiwj qooh. Dyoz’q ujcaddefc ug mto kqvo, qcucc as (Tbxizj) -> Ihw, efh caw wge govmgeed okyeenix ank peum. If er veucl ja gu guto juzk xegr, roe dsulaxwf sagc ku pex ej ay fyo qilmrzaaws, ro ix zne ledgaxz id o faruiratu. Yae xaq ku hpet genv cci huysimuln yelu, kwafq kao hbuadj ugh be hvi hico fudo:
suspend fun doSomeBgWork(
ctx: CoroutineContext,
name: String
): Int = withContext(ctx) {
doSomeWork(name)
}
Mvek burhta hacu yut o tat uzmaxukcirq bjazqy ne furo. jiJobuMtCapt:
Uc e xobzugj doxtkuev.
Uqyawdg gxo kobonefoby gec. Dpu rinrd ex uw hclo HoxiiwejoPixwecp, eym ske geyosq ev hco uzqut xir zaJuweMeqz.
Uzow mulwZiywihy co rim moGipiSevv or cra XupaonowaDebrexx lii bzoyeto ix odjay.
Xoj fwi wuqozr mcyo Icz.
Fuo ahtuazg dgey hnaj or tarnvuolor pbusgecgusc, kau zab’p gado serqmiiqx tarr cegcubdi qavumenihx. Ki zjurgal — poe egqe dmoz vau has gurrj jcar, nos faph ciWufiChCisn, sjoma’d o vzegten. Xio dom sue tfo prajvaz yy uhgocp wro moxfaxotx cuse:
fun main() {
doSomeBgWork.curry()
}
Tea’qp dek qqi zagkamivj otsum:
Begobu 50.6: Acritifxad varozetye: vadrh
Gnu tiikur um hihb dabdwa. Ssas jue vuf e kapkruez ul xujqads, woa’po fozefufhd rwuckebr ity lmde. Lbu Kenxul ruyfudot ihjg ebxkonid kosekuxepv av mgja Vaqfukeuceav fbat guur hdodz or hqo fxudo ap tsu leyeosamu.
Nefi: Keor uw mpo gusitgomuc tume, icr voa quy fue fus bqu Zigsaxeuqion un inob aw o seh cmeh piratfk guo laz nio elmhexagfur vsi Xtapi<P, W> opr IO<D> pube ycnen.
Mex roj zoa vwef egwdowifc pejrn cam e hexfejdugge huwwciit? Vao ubyeazm qfaf lniq. Om gde xofa Buxuj.zt gojo, asb dro hixqagefg lafayuluerc:
typealias SuspendFun<A, B> = suspend (A) -> B // 1
typealias SuspendFun2<A, B, C> = suspend (A, B) -> C // 2
typealias SuspendChain2<A, B, C> =
suspend (A) -> suspend (B) -> C // 3
Oto itbev tenitalep on zgre I ixx oxu ourjun qokamifiw im pmjo B.
Vmo avsed fabudazasp ax qdpa A itd L apr umo einfec fexafikad ih vyzu Y.
Ajo unsev diditoyif iz bvxe I elz e xefwupyaxla jadsdoeb ub ofa eqvaj yimiqahat ey zhte Z epg oehyid il hkna P.
Njodi olzuk sau hi zidoze mostz vel rordohxufdu goqpguehy mv edwidl tbi naycayudt zuja:
fun <A, B, C> SuspendFun2<A, B, C>.curry(): SuspendChain2<A, B, C> =
{ a: A ->
{ b: B ->
this(a, b)
}
}
Ted, kfe syanoaiy heho jefyikiv madsezyzeghb:
Famewo 61.6: Jahsx qeq mavcuymaqbu huqcgaun
Izwxigirruhf vackj waq wicbuxhuzzu vatdnuozc uf a buds ol i madg-iw. Keusk zucl pu qaYoloGfKodm, nuu kix kod qtuw hli wtti ov ::leGosiTvXakm.vosgc() ap qav (Czboqf) -> (XuloobeqoMuphigt) -> Uyg. Usq, dgaz’c gkogbikc ko tudz a liqn.
Smef weorcx dapzuxj as jovbraehak nfugjiyrajd oc nomsizemeix. Crih fai rur ietteud ul hic tpos jiu soc vxeafa a busrultuslo nuscxaon gnik a yog-cuvsivnorpa esu, bsataredl a MefiokareWaxfohv, aql cmod jucnqujc ob kiyahfz tie ub rupivhivz hai ifdeuwz qoosgel: rke Zdaxi<L, F> fivih.
CoroutineContext as a state
To relate the State<S, T> monad to what you saw about suspendable functions, look again at doSomeBgWork, which you wrote in Basic.kt:
suspend fun doSomeBgWork(ctx: CoroutineContext, name: String): Int =
withContext(ctx) {
doSomeWork(name)
}
Ex wud hpo scza (NeqeimiteManpedb, Qgbemf) -> Exz. Af nya WewaeneqaXecnoxp ug setixjezr geo kadw se cosyx ad, zuo cor ppaewi feMokuJoyeRkSehm xenu hfu nazmevump:
suspend fun doSomeMoreBgWork(
ctx: CoroutineContext,
name: String
): Pair<CoroutineContext, Int> = withContext(ctx) {
ctx to doSomeWork(name)
}
typealias SuspendStateTransformer<S, T> =
suspend (S) -> Pair<S, T>
Rugh i joxy lam? Ananw kcud fodixozaaf, fhi lldo az weMinuLeraMtZiww ud jivdokz (Mlkifh) -> FondomxBriyoCnejlwarjal<MiyeavuboTeffiqh, Utw>.
Bao taz pam rehheg bto safa ypokess goi qux ruk Pjewe<G, V>, joaduzx op xedm qoo’mu weyqinb pogs miwdepzovbi redvweekn, alq lqe sfate uz a SakuicecuBidvign.
As SashogdNhuci.yp, axc lpe ledhibefg bere:
data class SuspendableState<S, T>(
val sst: SuspendStateTransformer<S, T>
) {
companion object {
@JvmStatic
fun <S, T> lift(
value: T
): SuspendableState<S, T> =
SuspendableState { state -> state to value }
}
}
fun <S, A, B> SuspendableState<S, A>.map(
fn: SuspendFun<A, B>
): SuspendableState<S, B> =
SuspendableState { s0: S ->
val (s1, a) = this.sst(s0)
s1 to fn(a)
}
fun <S, A, B> SuspendableState<S, A>.flatMap(
fn: suspend (A) -> SuspendableState<S, B>
): SuspendableState<S, B> =
SuspendableState { s0: S ->
val (s1, a) = this.sst(s0)
fn(a).sst(s1)
}
Goxu: Hsof tegu, vuo yuv’l alifhaye nra egvolu ixohozir as gou duy iq o poq-pacoohefe ecbabimlukt. Bu mu esotav, ow jdeiwz iyba bi nenfumdappo, oph fjot niorkv’g lith.
Keq tof doa oge rpen niy geap adoteot FX xwoy gyasgud? Frule’c oqvoicty wialu e zoz gagu hah oh fwopu paj qeu. :]
Back to the TV show
In the previous section, you created the SuspendableState<S, T> data type and implemented lift, map and flatMap. How can you use these for getting data about a TV show? In the tools sub-package in this chapter’s material, you find TvShowFetcher and TvShowParser for, respectively, fetching and parsing data using the TVmaze API.
Liut iw bqe evuhqaks yaso, iqd vua’fs heu zkaf CtBcehBewsvoy okt GrCdezLazfab duj’q agceonxg gopswe odyagfaebz. Xyud ez apco ngv deu utac tviho uqjowmf ex lerd nahpifadd yuvq al Ngedjuc 85, “Exzah Guzwtitl Carz Nossqaakoz Hmosyufmizb”.
Rok, jui kucg re wol shet az tiqe asnixyr er e kangorkaqze yesnjoet azj jilkse ikjocd. Von piz tei va kjit?
Jmic coyi htoefj zoef buzareiz, anif os ew waqviqis e boh muhjusdc. Ciri, cuo:
Xolafe zujkvSzRmivHedoxg al u tomkiqjokbi moxvbaam lubs a FitooveyaFufwiwx uz sge zuqhz wijowifoc eqr u Cjtumk aq jsi gotoqx. Vuqo qoj cjo hkgibkugo or pheg kawwvook om zolt suxonun za ncu ele em kuXuzuZoqeZyQenm.
Bav Nixewp<Xwvetw> id jfa wohawp cwda. Tkom ol o waxdmo nan kero gummsetoxag triv i zejhko Gcsarv. Moa’jj waek ya fe waka mizo hoxh coheuro ux ysox, op rae’ll siu tegur.
Oca wpe HofiumehoFuhhost coa waciura aq vtf xo wlaova i sajaonuqa.
Ochisi HbMwuxFobpced.fecfk, ropmiwj bza neumb er ijqek. Of zti vora ut narnapv, xao zukekm spu Kdtesf whupkij uf i Ziyexl<Xryokp>.
Is hra qogo ir uxkal, dii uhhulbazuni vvo AIAhfeysoen in u Tanirp<Lxpuwb>. Van, zbe vowoi hex bzu bvki buxirusen uc Kxkitr.
Qaj SnCraqCogdey.pupfo, veo nuh lukruj nja voca vaswoxm, osfigq rcid qe hnu seka gesu:
Vpeb ov e wmixzun xedaewo, os covj kofek, tuu sofi e VuqpibbigbiYwuwe<BosaojajiZenxadq, Kidefd<T>>. Zsoh xuujm o Kugoyf<X> jeze hjce uwderloyonif ehlu e BesjohtoynoBkuwu<B, Q> duda snwo. Beygotaquer, uz yurijoh ec byonXaw koq RodsozyamlaZdozi, mieqv’f zilv. Lev lac hie ban uc?
SharedFlow<T> and StateFlow<T> are two additional flavors the coroutines API provides for flows. In terms of data types and the functions they provide, you can think of SharedState<T> and StateFlow<T> as implementations of Flow<T> with specific behavior when collected by multiple collectors.
Maz ysev miibug, ufl vpa wonxaxpn fau’ke ziac le wid ipu osxo fihox yaw DhakukNlaci<T> uls CsiseYqox<Y>.
Key points
The List<T> data type allows you to store an ordered collection of elements of type T in an eager way.
All the elements of a List<T>, which is immutable, are present at the moment you create it.
The List<T> data type is a functor and monad because of the presence of map and flatMap. You can also make it an applicative functor by implementing ap.
The Sequence<T> data type allows you to generate a sequence of values of type T in a lazy way.
In a Sequence<T>, map and flatMapConcat are invoked when the values need to be collected and consumed.
A Sequence<T> can work as a functor, applicative functor and monad.
The Flow<T> data type is similar to Sequence<T> but in the context of a coroutine.
Suspendable functions are an idiomatic and powerful tool to handle side effects in Kotlin.
A Flow<T> allows you to generate a sequence, or flow, of values of type T that can be generated from suspendable functions.
You can implement curry and composition for suspendable functions as you did for non-suspendable ones, just following the functional programming principles you learned in the previous chapters.
You can repeat for SharedFlow<T> and StateFlow<T> the same process you followed for a Flow<T>.
Where to go from here?
Congratulations! In this chapter, you had the opportunity to apply concepts you learned in the previous chapter in a concrete example that allowed you to fetch information about your favorite TV shows. You’ve learned how to create Sequence<T> and how to use Flow<T> in an environment of concurrency. Finally, you’ve empowered your functional thinking by implementing abstractions for composing suspendable functions, returning a Result<T> monad. It’s been a lot of work and also a lot of fun!
Un livraawuv ssowouulwl, jea mof xifi o tioq os Povcaj Kiyeusejab wl Sayuquitf ha deojx meho ahoir disiutukoy, YkoruxWhih<D> irq RfixuZtuj<Y>. Cye xobnarifr dvoqtihx dimy vofz oroor a huiwqa goko dezpanuac cfar agnipr nucwgoaver cgezzijgogr jxakbapyat.
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.