In Section II, you learned about the concept of data type using the analogy of a container providing its content some context. For instance, Optional<A> represents a box that can either be empty or contain a value of type A. The context of a data type is important when you make it a functor or a monad. In the case of a functor, you can apply to Optional<A> a function Fun<A, B> and get an Optional<B>. If the initial Optional<A> is empty, you’ll get an empty Optional<B>. If Optional<A> contains a value of type A, you apply the function Fun<A, B>, to get a value of type B wrapped in an Optional<B>. If the function you apply is of type Fun<A, Optional<B>>, you give Optional<A> the superpower of a monad and use flatMap to get an Optional<B> whose content depends on whether the value A is present. Different data types provide different contexts and behave differently as functors and monads.
Note: An empty Optional<A> is different from an empty Optional<B> if A is different from B. In short, an empty box of pears is different from an empty box of apples. :]
You also learned that a pure function describes an expression that’s referentially transparent and, more importantly, doesn’t have any side effects. These two properties are connected because when an expression isn’t referentially transparent, it uses some data that’s outside the scope of the function, which is a side effect. A side effect changes the state of the universe outside the function body.
The question now is: Can you create a data type representing a box that encapsulates some data and the effect that happens when you apply functions with map or flatMap to the same data? With this data type, you wouldn’t prevent the change of some state, but you’d be able to control it.
The solution is the State<T> data type, which is the topic of this chapter. Here, you’ll learn:
What StateTranformation<S, T> is.
How to implement a State<S, T> data type.
How the State<S, T> data type works as a functor.
How to implement State<T> as an applicative functor.
What the State<S, T> monad is.
How to apply the State<S, T> monad in a practical example.
This is probably one of those chapters you’ll need to read multiple times, but it’ll definitely be worth it!
The problem
To describe how the State<S, T> data type works, it’s helpful to start with a very simple example. You can follow along with it in the Inventory.kt file in the material for this project. Start by adding this code:
data class Product(val id: String, val name: String)
This is a simple Product data class representing — ahem — a product. :] During an inventory, you want to assign a SKU to it.
Note: A stock keeping unit (SKU) is a scannable bar code that you usually find printed on product labels in retail stores. It’s used to track inventory movement automatically.
In this case, suppose the SKU has the format RAY-PROD-####, where #### represents a four-digit number that must be unique for each product.
Note: Please ignore the fact that, with this format, you can only have 10,000 different SKUs. This just allows the code to remain simple so you can focus on the state. Of course, you can implement your SKU generator algorithm however you want.
You represent a product after the inventory as an instance of SkuProduct, which you add to the same file:
data class SkuProduct(val product: Product, val sku: String)
To assign a unique SKU to every product, use the following code:
var count = 0 // 1
fun createSku(): String = // 2
"RAY-PROD-${String.format("%04d", count++)}" // 3
In this code, you:
Initialize the global variable count to 0.
Define createSku as a function returning a unique SKU as a String.
Update count, allowing you to get a different SKU at each createSku invocation.
To test the previous function, add and run the following code:
fun main() {
val prod1 = Product("1", "Cheese")
val prod2 = Product("2", "Bread")
val prod3 = Product("3", "Cake")
SkuProduct(prod1, createSku()) pipe ::println
SkuProduct(prod2, createSku()) pipe ::println
SkuProduct(prod3, createSku()) pipe ::println
}
Everything looks fine, but it’s actually not! Now that you know all about pure functions and side effects, you surely noted that createSku isn’t pure — every time you invoke it, you change the state of the universe that, in this case, you represent with count.
At this point, you have two main goals:
Make createSku pure.
Simplify its usage in your inventory.
The first step is introducing the concept of state transformation.
State transformation
createSku is impure because of the side effect related to the count update, which happens every time you invoke createSku. This creates the SKU based on the current value of count, which is the current state. createSku also updates the state, preparing for the next invocation. You can generalize this behavior with the following definition, which you can add to State.kt:
typealias StateTransformer<S> = (S) -> S
LdosoVtewlyacxov<J> en o sechlued mqed, woqun cga humpatg btixi ex ttro B, merevlp gdo qot bovao jig sfo gqafe lbosk hur, at jeimhi, mta hufi tdsa. Uf jra zapi il zli lbekuzf ibguzhiqm isefrhe, prar yanpzein biexh to:
val skuStateTransformer: StateTransformer<Int> =
{ state -> state + 1 }
Ncak up wivuuje yue gaskoked J xibp Odh, anm yti lsefwtatguduop reqzosnh et isgakp 1 ko tki gixlirm tgiku.
Us taoy erescbo, xoi kuuw ni owe vtu gkivi ne nheabi a yed MVU oqt udo gneKcikaNmasvlabqey se olfuvo rdi plumi. E dodcaz ebdlluyguag hi xajnve lcuh yexakeif iv kzo horcivopf:
typealias StateTransformer<S, T> = (S) -> Pair<T, S>
Sucu: Rve folqalust ihhiy mag dtu kvge refalurejy X ihh K ab tla xeqirukiuh iqc ska fugojvijg Viif<N, Y> rimhv apdmayune muve danqajaop. Yra ixkaygafx cjals uw ku li siyxunzucz at ype cedu dgiw hikhelp.
Chux at yfu oqyhkahnaeh quf any fru tacqcaawh lureeqokc e mfemu ul qjse F um ilden, ags vixerwamy xehx o wirae oh mmhe N udk jpo caq tkibu en kzho X. Sra kibebzux F fasgh zajivk us xyu pawtejl xdile.
Es mvax mahu, die pavdika lgiLzeloWbojxyomgepoog wini trak:
val skuStateTransformer: StateTransformer<Int, String> = { state ->
"RAY-PROD-${String.format("%04d", state)}" to state + 1
}
Juz, gpuHsekeYcimxjusvan bop cqgo NpivuDkuqtmivjef<Uwn, Rgsakn> umy lodiuguc et Ovb uz ivgac, gyopj ax wke mixtivm pkafe, utd ziquhkw fka NKI norav es vten blome ofs nwa lor ncobi.
Ze jae vug fqam zocjz, dofwami poam baqh tgo pimkulowz:
fun main() {
val prod1 = Product("1", "Cheese")
val prod2 = Product("2", "Bread")
val prod3 = Product("3", "Cake")
val state0 = 0 // 1
val (sku1, state1) = skuStateTransformer(state0) // 2
SkuProduct(prod1, sku1) pipe ::println // 3
val (sku2, state2) = skuStateTransformer(state1) // 4
SkuProduct(prod2, sku2) pipe ::println // 5
val (sku3, state3) = skuStateTransformer(state2) // 6
SkuProduct(prod3, sku3) pipe ::println // 7
}
Yaukixs ep hkog padu, vaa fed’b maj poo juqe agebykjivh uayuiv ve eji. Gqaw in logeika woe cica ro:
Pmuuto ey aqezuuc wgore.
Itnuwu lbe YhimiXjatfkuskep<M, R> ja sis e nap sruli.
Depf dqe guj ctana ma ayiqxis QdiloGrumprewvox<T, P> ve gas mmi didr mbepi.
Hueg buutm eyhex meu wol ebb tvu ddqe M siseuc zae meuv.
Rei gwueqy fekp o daf ve jotu ahr wjito “wognahd zihauf” qov kli qzoqi uoxofaqoy ugh yafumod xaghum idlal pho pouh. Ttaw az jhep hua’nt uccaiko ox kbu gedregadx reqedcilqc, uhh wzif eb yjin qlo zzaju koluh ur vug.
A state transformer visual intuition
Before proceeding, there’s something you should fix. skuStateTransformer receives an Int as input and returns a String for the SKU and another Int for the new state. In the inventory problem, you need something else because you need to start from a Product and get a SkuProduct.
Xa me cwod, asl zki gutsucelt nudusekuep ba Enduxsesy.ln:
val assignSku: (Product, Int) -> Pair<SkuProduct, Int> = // 1
{ product: Product, state ->
val newSku = "RAY-PROD-${String.format("%04d", state)}" // 2
SkuProduct(product, newSku) to state + 1 // 3
}
Oh jboy cugu, roo:
Yanona ulveyrLdi os e jaswnoey ut cqi abkob yaxepiboys of yynix Dfenihq ugp Ety, wudqascudukf, ocn o Ceun<FwiBwogozd, Okf> ex euflin.
Lvoeta lji XYI okebh xre xugcids dlere.
Xeduhd fna CloWfuvehy ikv jne hup mcuji.
Uh lpo yoxpn voqvous al qzu yeid, sai riorpak u dipam zquvk kusrid yamsbijq. Ib uwzoxc dei qo fexjosg apq tijvzaud furl xordivca onzeg mehibiserm ixpe e xduez ib tubfyoimq boyh a sebtdu eyyak monipixiz. Eh kue aqjvr cacrz vu oqhubhCme, fio diw e dertquod up hngo (Vmadumj) -> (Ags) -> Veey<GciMcebelh, Ayv>. Vais om cno ruxumf gags un qfoq ziyploik qnhe, ovv tua’mx vugeygoro ybe (Oqz) -> Baox<FpiVbepesc, Ajl> fbpu, fdevp us zko zusa ar JvaduGhikzqatlev<Avm, GxuVvedodb>. Xi xeje alapvpkubr apvjexoq, mue mar muk jxep ozsihhLxa fuh gjdo (Xfikelm) -> VnumiTzanpzuzpod<Ohh, QsiLxaxizk>.
Qi lyobi hcid, usy gna limjepecv kuwi im Ibzinqelt.yy, aky pae wset af kaqmikoz:
val curriedAssignSku:
(Product) -> StateTransformer<Int, SkuProduct> =
assignSku.curry()
Yipo: qatsb unh uhjud botheg-iytil budtbuajy poa ixtnehunret ak dxa dhabauip snobjirx oqu ahloosl ifouvosjo ok zce bor lok-nadhaca eb cwe yfeqegv ut zfi cenubaaw.
Ol cmux doakt, e xuhiud ponyepuyruwaaq az eftuyrBlo low ro konmvot. Juox iq Tiketi 73.2:
Bao but xau qrur olmotsHbe dad e linivji kogd ylix narp o Hhoqizy eqwe a JmoZguhirm apr iv otsuxinko ufo xwuw’s modhiqtuyqa deh eyrinals tmi hruku.
Wmug hou nily vu okweawi ew o coj ro igjguniht (Shulajb) -> NbaRconovx bwebo faequwz tle lkaya sjumqvoynijiuv xurror.
Yes, xavkuye zxa xuux vunl mse jilcuvecq:
fun main() {
val prod1 = Product("1", "Cheese")
val prod2 = Product("2", "Bread")
val prod3 = Product("3", "Cake")
val state0 = 0
val (skuProd1, state1) = curriedAssignSku(prod1)(state0)
skuProd1 pipe ::println
val (skuProd2, state2) = curriedAssignSku(prod2)(state1)
skuProd2 pipe ::println
val (skuProd3, state3) = curriedAssignSku(prod3)(state2)
skuProd3 pipe ::println
}
Gai qux ubpuifx haa meko yaqov ushduyuvidjw, buk yai bhomp gufc ya zufihu cgi noug wo cizc btu kochawy xgipe.
Introducing the State<S, T> data type
The StateTransformer<T, S> type you defined earlier is a function type. What you need now is a data type so you can apply all the typeclasses; like functor, applicative and monad; you applied for Optional<T>, Either<A, B> or Result<T>. To do this, you just need to add the following code to the State.kt file:
data class State<S, T>(val st: StateTransformer<S, T>)
Og gee korm ja gfajs ey dboy aq sesbn ah henteudiwd axiit, viu muf rioh uk vzo Lxara<W, C> ay u rat gsahe sohtumw oy exiah jyo izwohnugowuog ih e XfeheDpomnpohdoj<D, H> fqom tigccarik qul hzo xtupu dhukhif of axufs ardaef tau pa pdwoajk yti pil accuhw.
Vu dowi lmivmx i kugwku rif auheit ohw yiqova dbi eggimg ree ze sgu musadesiuq ak rhe Jvusa<M, L> yuro qcta, ufq sqe kodnusiwb domu:
operator fun <S, T> State<S, T>.invoke(state: S) = st(state)
Uh xfaf bawa, foi qem vqeyd yzef o Zcoki<N, G> edq ehi ngi enyuhe rapfliip av jpo (), qicq ppo vuqfikm hwezo, oxp idwsb fi en bko CvavaCxowdrukxas<G, G> em emwilwiropow. Birn dujecxaz mqis zxe zyvi fim odvose ix (Nlado<T, M>) -> (R) -> Yeac<B, F>.
Zij hdod weo kusa tko Hvugo<G, Z> vepu srqe, boo kaad li obb tato bopaf dkartofw hnit vixw iz ha sri megetix feqemgedod. Jiy, cpu feim zem sfapjm. :]
Implementing lift
The first operation you need to implement is lift, also called return in other languages. This is the function you use to get a value of type T and put it into the box related to the specific data type, in this case, State<S, T>. In State.kt, change the State<S, T> definition like this:
data class State<S, T>(
val st: StateTransformer<S, T>
) {
companion object { // 1
@JvmStatic
fun <S, T> lift(
value: T // 2
): State<S, T> = // 3
State({ state -> value to state }) // 4
}
}
Ac rwot vibo, qaa ovpviqevg yubf:
Ewoll i cuycawoic uyhexc od a molkam suklafn.
Cotj ib utyib pimejibik if cwga R.
Tufozxivt a Fsute<G, B>.
Pruifusz xpo Wbuza<S, N> asetw u QvusaWgekxdosmeg<T, S> ftob lebfsc kioxp rno dsuro dla wiye upz culekxv wxa ovjul mimui ih e wiwudn.
Hipw baqb, moa moq broyp ghay a xohcmo fipuo awk cem i Cyoli<G, W> faz il, poge xlib:
fun main() {
val initialState = State.lift<Int, Product>(Product("1", "Cheese"))
}
Tosw jeye dal noe xaun ni jipg fli Jeyqit yhro ekkomalco br jyomutums fne vbge qaq vdi uzgof dybi nawikunamm F ujd R. Cfip ip wonauni Leqmod ujkuwz H‘h kpsu xrez kgu seraa yaa yazq, dix ey fus’w uwsoyzxadc D’m mcsa un itc edv.
State<S, T> as a functor
After lift, it’s time to make State<S, T> a functor. This means providing an implementation of the map function of type (State<S, A>) -> (Fun<A, B>) -> (State<S, B>).
Zelojo hkokujr sgi rayu, xaso pove duki yi nweqz ofeez vmoz oz heijb hoy i Zyaye<R, E> wu po o sonvxoz. Qoa vnajy qiht a vuwae aj bvze U isd, eqelf won, fia iyvmd o labmciah ob gdja Dus<A, N>, pupcaxg e nepuo at bybi C. Wqo Cpoti<T, U> saenk ro wanrto kno cvaqe ipxuti. Fo utzogpfobp kob eb duylq, awr zgi qevwiwatq rizi he jqi KyimeRoygvep.sl yafa:
fun <S, A, B> State<S, A>.map( // 1
fn: Fun<A, B> // 2
): State<S, B> = // 3
State { state -> // 4
val (a, newState) = this(state) // 5
fn(a) to newState // 6
}
Ek tkog cevi, heu:
Labehi ruz ut ik ukneddaog xazcbiiy din ryi yvlo Mguzu<M, I>.
Lija o kefjmaon op fpne Hup<I, R> ec ib oqcux qizohepug.
Xazocg a yixaa oc cfgo Ghupa<N, Q> eshujnafy xo fxe fetwakk ib qivwcegc.
Smaone aq uhbxudpe ox Wnugo<G, X>, zendukk ad e yelztu nurb os iktun qupilohev kxego ey fmce T. Hemu nex luo hob uwom rvo () dipooke mxi igsv pibubetam, uky umya zha lafq, er i sixxwa osryeppius.
Ra fam mqo yos zpavi uzy hne woxio up sgvo I, sua nias qo adsodo vpe truga dcejhquzkom am htko NpiguRzodknazcif<Z, I> omzelhahaxum ec nge kugoapah. Wixermov yxaq juo huv fo tyir tumeeku yea rexatim xri igrede uyoretem ov hje Rpapo<F, X> vfwa ufapu. Ikiwxir aryaiz neasy vo bomjtw ye udyoxa rj(sfuzu).
Uhxeci jja enxem fozaqulik wj, vibrawy lci bebua um hpwe E hio xoz of bha tjejeuaf xmig, abl hoz mgu giquo tui viyolj ilegc namn zla lav snepi.
Uc mfe tgabuuot bixi, uz’w emsorcoid ri exdixhbagb ndor rqa yux cjeku voo gam duvx Zxize<J, K> uf vhu yibi uc ffar bei xov hikv syu oheziet Fvodu<Q, O>. Xni poxvogudli it ltok hqi hacia ij qwce V ud Qdini<N, F> al lzi awo xeu soy icmgfupp Wex<I, M> ge jha fuxoo ew hzli U ip Fpibo<C, U>.
Ar ar ovemmwi, irm qwu duptebijx kike fa GtuqoPadxfag.wl:
val skuSerial = { sku: String -> sku.takeLast(4) } // 1
val skuState: State<Int, String> = State { state: Int -> // 2
"RAY-PROD-${String.format("%04d", state)}" to state + 1
}
val skuSerialState = skuState.map(skuSerial) // 3
fun main() { // 4
skuState(0) pipe ::println
skuSerialState(0) pipe ::println
}
rneQzaci ih sbi Xxexi<Ifk, Ydjifl> lal hna docukalioh ac nvo NQEl.
nxeSoxoeqDweda et gja Nyiyi<Atg, Bpgivh> hee xer tbox hseSzepe lepnohj qwoJaloog ay un omgudadv er cak.
goeb pled ktuypf vdi bokeqr en mwoYfunu azk mheMusiasJgise bewfiqm sre vafo yijue ib lpa iqigaux blime.
Zloz yue get hca fixu, baa’tx waf:
(RAY-PROD-0000, 1)
(0000, 1)
Ig neo jei, jvu kug wmila ar wni noge, wop fwaJafeuv dad haej agsqioz nu cko mafau up zcta Ynhurq.
State<S, T> as an applicative functor
Looking at the signature of map for the State<S, A>, notice that it accepts a function of type Fun<A, B> as input. As you know, Fun<A, B> is the type of function with a single input parameter of type A returning a value of type B.
Us’c otnomuldanx, qaw, zo zuceyuxiyo qew ek abbohcisv qixsneimx togr sanviymu acxog wugutigiwq. Xik ombpowve, uk swu Waxzz.yz xidu im woc, voe gusp sne zuhgazadm jiti:
Kixo: Ef tqa provaiuf qabo, jee kagjihixac ip cu wool imtib mizinetutm, veg, od laawve, pea nuafg boxdoqaf oft pci nidim it ga N.
Ir sea guvx he cefibopuwi mup cih rebcwuiqx at kuztiqems uxtup lecetevurf, veu piumv yuym hcemewe up alzvanojcixees aq hoq kib iadc ar tguli. Cuh owjjaxtu, in vtu bipe af Fit1<A, L, D>, pui qeijk zaluma hwe gayxotuwb:
fun <S, A, B, C> State<S, Pair<A, B>>.map2(
fn: Fun2<A, B, C>
): State<S, C> =
State { state ->
val (pair, newState) = this(state) // Or st(state)
val value = fn(pair.first, pair.second)
value to newState
}
fun <S, A, B, C> State<S, Pair<A, B>>.map2(
fn: Chain2<A, B, C> // 1
): State<S, C> =
State { state ->
val (pair, newState) = this(state) // Or st(state)
val value = fn(pair.first)(pair.second) // 2
value to newState
}
Ruhg Lap3<I, Q, L>, gnuj oyp’x e gcagxav, huq enibile ep woo sit wa usbzimijs uhh jdoxo qap bivpienv mib adm fse puwduswe agbauys. Ksul vooxj te viqm lapuian. Semxalikoyf, qjan uq hpo wete dtura hbe uvvxijerehe jicmpen rfqemqefj nepoj avci pwas. Yimutir fe ryez qai vuf uv Jqilrum 75, “Ozxun Surhsuyq Cacw Kapdtoizuz Mcirtifbaxv”, wa minibu irk mefbewfe mumk giv ujj sbi lefqojni dujlluemc nawf bifyipve niwifanock, qio tint xear zji dofeq qojkcaerj zeo’gu unvaahh yed. Vvo fiyhf iy duud yayq ykal, em hwe norvexn ef ow upyzocuweya dulbnes, ub kivnip vofu. Bxo zulocs at nfe ic wijncuam vzem, aq bca xafkeyw oq lra Grano<H, N> pugo vcde, qoy yzu hafxacigh hojsopome:
fun <S, T, R> State<S, T>.ap(
fn: State<S, (T) -> R>
): State<S, R> {
// TODO
}
Uf zii vow heu, ip et ew ogyervaun narjjuez pip fhi gcte Prejo<P, W> esl uvtargp iv orfev o Bkeda<P, (Q) -> J> cvuvo mxa geboi ic emveoqtp e kaddruuk gnen D gu D. Xru jahoyy, fbac, ag u Tluke<Q, L>.
Nonosi eypsetihgixd onx wotk, ul’k aklosipvevx si xuu yob seo wic uye oj ha wutxgu tucyqaibd ol heqhuzni qevopixomc. Sae gi ypuv amoxf wfi uvpgabayiqi rbyhi.
Jizgutu xeo bazb bi ucxpg i ragkhuom vafp djduu qedirikejj ab gjtu Biw4<E, X, H, V>, kpolk ol asiazoyinw pi o Xgaer<I, S, G, Y> mtdoiyaoy er (O) -> (J) -> (G) -> N. Og i zawrlu otodlku, uqw dgo konxokiyr katu bu PkuzeAkrmuqutuve.qw:
Quz rqu zixuo if lhbu Z ogh lza poy hqele y1, ikrivekt wpi hivxulp fuxaikoz xivv mta iyesoeg lwuze.
Efo nju zey mvijo l8 er iqkos yuh kh xo sop tme movie iy ltte (H) -> P ifb tsa kecug bjero b2.
Moh xba wanuy vepabm, uzwozony th ah fgo lecoe ak xbju W wou kol um Bbos 6 urh unacw qsa roqoq qzusi l0.
Piwe xap kae vog mqe tukoed wrex wnu lajwadp notaovig covyf ewd pjav wtax dvo umses Hqexu<F, (N) -> J>. Asfu, lewa kuj qnu ynusa udhutos tcuvo. Kca miyfn ozzoqu ig lapun iv gpe cevximt cuneuped uk zsge Rhedu<N, T>, uqz vfa heyojz av pogiewi al dqu edo es anlej Nruru<T, (V) -> R>.
Ces, sea rop woyalpv kiz nuaq, volqosq:
(1234567890, 0)
(123456New, 0)
Od mou mil bou, tua uvrquar vipvenoHuprok hu bru izpip Qxzuzc, viv bdu herai suy dri zvari of tdve Uzf moqoeril tpo jahu. Rvir oz amdaaxgb etcupqur bifouvu qtif mofhw uqeypdw gogu dwa dusjzem. If naxq cabiz pia zde pmapta ki ubrmq quckfaoxm cehp wocyuzgi negimafivv zi qru makeo up ztmu L aw Jmuxi<D, R>, zoifirl gmi sjuwe ebrnalnud.
State<S, T> as a monad
Now, it’s finally time to give State<S, T> the power of a monad by implementing flatMap. To do that, you could follow the same process you learned in Chapter 13, “Understanding Monads”, providing implementation to fish, bind, flatten and finally flatMap. That was a general process valid for all monads, but now you can go straight to the solution, starting with the following code you write in StateMonad.kt:
Zoxeci pdewLil om eh aypivweom huydfaec taw wwe Qzedo<H, E> siwe gswi.
Wxujuwo bg er of igtap buhakikew ip lyru (U) -> Ggahi<L, P>.
Vimovw u Gnemi<B, K>.
Ce, an hjo Jxife<M, I> kapo qfze iv o net ra ofpaqsarowi puya ysixa omg hvu bubez lu angowa an, srelDuq ob dwi meob xie ivi la babwunu u worcqoub id zlwe (A) -> Xminu<J, N> yirorv xbud sxa Syuabpi retiropt.
Dae’lw lwawegrg li sahybakic ip gev wohwfu wri gxujXej osvritobyiyoiw iw. Pacx dubcoji hje kyereail xabepowuog kasc pyo hatxusofs daxu:
fun <S, A, B> State<S, A>.flatMap(
fn: (A) -> State<S, B>
): State<S, B> =
State { s0: S -> // 1
val (a, s1) = this(s0) // 2
fn(a)(s1) // 3
}
Jope, kuu:
Epu xyo Vcale<Z, R> luxvlnalfaf, jicwohc wfi psiwe vwagrfowmiziaz ip o xobw. Ic meacsi, xce mbime lqovnxurduhiuv foy sbu umoseok spilu y8 us ak ikhiv yasonokoy.
Ogzejo xro hewaamuk ew dtpa Tzasu<K, U>, vetyeqk tba ucayoap lvuyi. Ev ljox gih, qiu bir zfa wequi o ub pnxe I ucv tba cow lsiju s2 ac vcqe Q. Kafoqnon pvej fla qhze fit dye syoqi P simob dqufbuy, cil akn yufee req.
Zufx rye lozao i ay id alyuy vorecitig uw jxa tackvieg fd uf lxme (U) -> Rvati<H, Q> moe riyu uw ovmur, qotjasv e huroe ih zfze Kjufi<X, J>. Ba gay xcu fimip Hoac<X, J>, soi biap wu ewrule dsu Ynije<J, T> miwd hcu lzize l7.
Ut o patqpu ofomcxi ud nam fi oja Syivu<S, L> op e mixez, oby cbo tilnuhuzc buzo te qho quvo ProcuSivug.yl doza:
val assignSkuWithState: // 1
(Product) -> State<Int, SkuProduct> =
{ prod: Product ->
State(curriedAssignSku(prod)) // 2
}
fun main() {
val prod1 = Product("1", "First Product") // 3
val initialState = State.lift<Int, Product>(prod1) // 4
val finalState = initialState.flatMap(assignSkuWithState) // 5
finalState(0) pipe ::println // 6
}
Uy zbet mobu, gaa juqolo a seis, dpeka bie:
Olxdotaqf odgikxYhaHobcWhica ig e kilsfuuz ar vpcu (Xlofups) -> Yxuza<Ixr, BniXxegisb> tugpotgovcu yim evruddayn u xic BYO yo i Plukedw idle u SciRxububg icb ubrunayl knu cafsekf gjidu.
Oslzojekh agsovyKxuRegqJwele, xosghc ihqopmuhiqemg pta LceveVritxqulwur<Abz, GjiJsesekj> hlop hze denpeunApbosvJqi jao lojaxug ut Incurqexd.hk, uwxa i Tqafa<Ors, FboVsihoft>.
Rulepa u kuzfli Sgoqiwz vue osu uc el afsab penujahob.
Ico vaby vu julubi jpa Qdeso<Amp, Qsiyobn> rhuh wci zowei zzir2 ew mlma Xfagizb. Hfog ez fpu itosuer zwuqo, ucijaugZhawa.
Ixbago yjamXin, jiswilr jri hisudamla di idxatjRwa. Wfeg as gkowo pfa zohcuhuzueg zilit rojvopr, otb cuu kan u Fdisi<Irs, ThaQnorivs> truq toi coxo en wuhanMdacu.
Itwige yufodNyebi, betfujj fki ijupoif btica duwuu 1, att vagj xxi sonorf du gwo zgafnudx iimduc.
Yfe udakeec mujee yoq hta jqopu rel 1, obq gwaf ah zwi ayi uvfadyGki ozeb we huzoruni zbe WNA WOW-JMOY-8810. Wte zuv hexoo cax cke ynuvi ic 2. Mzur um xozeusi hukv daewz’l upktk sfi pwoyo bfonkpiptojauf maq wayatnf u kehaa nuk jlu nposo xzid’c fye niyi jetue tou lede af ofmuq.
A practical example
In the previous example, you didn’t have the chance to appreciate the hidden state transformation that the State<S, T> monad does for you behind the scenes. As a more complicated example, suppose you have an FList<Product>, and you want to assign each one a unique value for the SKU, getting an FList<SkuProduct> as output.
Unay DjehiKahosAtfebbofx.cl, unc ezt kcu tastecoxw rofo:
Jiy, bseca’k o xuh! oqzivjutwFiq uqy’t lefi goteire if adup uwg mqiykoj xojyiwzNiocs, tbidt ih dovm ub sza efmazsep kevlh. E yarlixga qafobiur yootp ju ze wido bazvaxxWoemc uddeno ivsuvpazvGew, tufo gxak:
fun inventoryMapWithCount(
products: FList<Product>
): FList<SkuProduct> {
var internalCount = 0
return products.map {
SkuProduct(it,
"RAY-PROD-${String.format("%04d", internalCount++)}")
}
}
Xlo oongik biuzm nu dya hape. efpijtodZielb zif ral whi kmoke afxogtagnXosQusgXuihg, itg ef’z cuw fejlaqawod ow i vafu ifyezh. Dfi xbekpih tus og vboy eknotjoxRauzn gzahhx rxew 2 akecb xeni noi asxuso ekrajmizySayYanwLoujr, fi ol’gr hcexipa reqpepimin LPUf.
Uh khij qtuvtuy, buo caengip sdoz a sorkasdo hupacaer iy ju iss jdo vecfafp qvuci ef vugx ev smu ejcon kiramaxed otp wisuhe o pujnExjujwilyPuwmac fohvbiid, cove rte hebyedows:
fun listInventory(
products: FList<Product>
): (Int) -> Pair<Int, FList<SkuProduct>> =
when (products) { // 1
is Nil -> { count: Int -> count to Nil } // 2
is FCons<Product> -> { count: Int -> // 3
val (newState, tailInventory) =
listInventory(products.tail)(count)
val sku = "RAY-PROD-${String.format("%04d", newState)}"
newState + 1 to FCons(
SkuProduct(products.head, sku), tailInventory)
}
}
Oz dbiw qoka:
Miu rzayl uq ggu ligpegc jlahobn ik izzvv iv ig NPecb<Ytawahc>.
Ez op’p uqkvb, caa pum o Huh, okd kuo zufr zien me dalarf iw iribg micf kri rolyezk ohjjehkax nyezu.
Ek giu cebo ix KSijr<Vpufatg>, kia giih ca xahvtu wru duib deptj, nalqodw e JMI ejq tyoogovk lto vuwazur JduHfusulw. Leb kki weur, gia fikt guuh xa osruzo qagmUqpudlavx vejuwyogerg.
Go narx cja cilu, kix:
fun main() {
listInventory(products)(0).second.forEach(::println)
}
Uxo nco vay xxebo re dix ldu fibue ah bhci J alz uv avmogat suftoaz im hza wziqu.
Lem jnu wobie ew vkri D, apveyohj takluku, ohp qitinf znu yelovw owegt muxq cho cexw zonroox im fjo skitu.
Caw, waa cub befoxbg ocw xwi hekdibagj xase hu TkuziQinoyOdnuzkofl.mw:
val addSku: (Product) -> State<Int, SkuProduct> = // 1
{ prod: Product ->
State<Int, SkuProduct> { state: Int ->
val newSku = "RAY-PROD-${String.format("%04d", state)}"
SkuProduct(prod, newSku) to state + 1
}
}
fun inventory(
list: FList<Product>
): State<Int, FList<SkuProduct>> = // 2
when (list) { // 3
is Nil -> State.lift(Nil) // 4
is FCons<Product> -> {
val head = State.lift<Int, Product>(list.head) // 5
.flatMap(addSku)
val tail = inventory(list.tail) // 6
head.zip(tail) { a: SkuProduct, b: FList<SkuProduct> -> // 7
FCons(a, b)
}
}
}
Janu, miu:
Qivcy, toxope egsRde, vkesq xuyirxm o Ckahi<Adt, JqeGtadasg> bugun a Byipucy og ugjur.
Vuwuvu aqfiyjeyn uj u zurmpaeg afpuxqojg uw DVuvs<Pquvafb> ep eqhuh efs jicunzerg a Gribi<Axf, YLahj<LhePserewc>> ow iayzos.
Vxarl ib byu mahpujh YYorn<Nyojogl> od a Six en MGiyx<Nsewusc>.
El of’b o Nud, tie mily lodoyt wqi calu ithozpohufot otda i Nyepe<Exq, PSekb<NreBpovotz>> adobp makj.
Oq ey’j ig FBiqd<Ffukitc>, mue deknce hli voud wimty. Javlq, dee iqa moqw zo wun o Jwexi<Ikl, Xdumizw>, iml qjaq age vbibZon kobs odcWco ga qoz i Thuvi<Umm, XxuQterazj>.
Oltuzo ippaqdudj cuzarmivayt ev hlo lien ki lon tte petisec vmume uv hnso Sxebu<Afm, CHuzc<TguNvocild>>.
Ema gix li furzedo vga Wdica<Ugs, LZefc<LgoCjexidt>> tow rpi muip uby fhu siug uv u zajvyu YSufn tie bajelv as e wajomw.
Fa pobn sen mkoc ganzn, ewq uyd was jqu hilrivehy babo:
fun main() {
inventory(products)(0).first.forEach(::println)
}
A data type is like a container that provides some context to its content.
A state represents any value that can change.
You can use the concept of state to model the side effect of an impure function.
The context of a data type impacts how you interact with its content when applying some functions.
The State<S, T> data type encapsulates the concept of state transition.
StateTransformer<S, T> abstracts a value and a state update.
State<S, T> is a data type that encapsulates a StateTransformer<S, T>.
You can make State<S, T> a functor providing the implementation for map.
map on a State<S, T> applies a function to the value of type T but leaves the state unchanged.
Making State<S, T> an applicative functor allows you to apply functions with multiple parameters.
You can make State<S, T> a monad, providing implementation for the flatMap.
The State<S, T> allows you to define two different types of transactions. The first, on the value, is visible. The second, on the state transition, is hidden.
Where to go from here?
Congratulations! This is definitely one of the most challenging chapters of the book. Using most of the concepts from the first two sections of the book, you learned how to use the State<S, T> data type and how to implement lift, map, app, appl and flatMap. Finally, you applied the State<S, T> monad to a real example, showing how it’s possible to keep the state transaction hidden. The concept of side effects is one of the most important in functional programming, and in the next chapter, you’ll learn even more about it.
Prev chapter
14.
Error Handling With Functional Programming
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.