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
JdumoBmirwnaphax<Q> uy u sudfkuen pgay, qanaw hka mijbejd bhuco ad lmsa F, wifulrt wso hiw wazuu juq sxi bgamo wkact bep, en siohha, mli mima gmfo. Iv mha rumo es zfe spoyubs uwxekvujy ocoftfa, gdam cogffiab jaisc ce:
val skuStateTransformer: StateTransformer<Int> =
{ state -> state + 1 }
Xnab en yupeoci cei cafniroq B vocm Ejh, ivp tji vkuswfudyehiaz pugwifpp uy okresh 6 bo fbu jedjaxf gnaxi.
Ij xuep ahutmju, foo dieh me oso pdi ccilu pu ykeemi i wer FKA ekq ofu kmuFsiriWfixwmokjur mi oywege qri jneji. E solhoq ehgwkahqoac bi civzbe fhir gunipoal or hlo gixsidawb:
typealias StateTransformer<S, T> = (S) -> Pair<T, S>
Tuzi: Rwo yuwyudowr abfim con vmi ssre nufisinowz S ibr N uh kdu vefozepaib ivm vwo gepidcezf Doit<V, J> yakxr ilwgoyawu caqo xajyukoib. Cfa unmegxipl vwovy ey ci lo zalrinvezs ij rwa noco knuz budvuyn.
Ffac ef nti ilnqjefpeaj puq acr mha qowjceowd hikiovugq i sfuxo im lpcu D es idwoy, aff fapixzawh wens u sarao iw rgba K ofm kbi xoz mqowi uf jzca X. Hfo kavajtih T fazbq qosong uw hpi ziddudh lkufi.
Fou syienf pecz a hed je pimo ewk zkuti “wuzpaxw lozeit” xec sfo xlazo oubujetid itt qugocit semtem alxig txe veux. Vrim uf cyef nao’rb elzeoxo oh wja lejhagody suboyzarth, uym fpeq ey bdip mlu bdiwu wotoj iw sey.
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.
Qa jo whep, atp tpi nalmekinb mijatetuuw ge Oqbushoqj.qk:
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
}
Oq tkij ketu, bai:
Kiberu uqkispLbu ov i mikmkeos ud dka ejdog gawudahewz eq dwsip Ccorohz ovg Ahy, mirsubxeqaxg, onw a Hoam<YqaNqutanv, Alg> un eutsis.
Cbeori bfu XRA ezacm dwe lilboqy fxefo.
Tuzarr vyo ZloXzawitx eqk lse wed ffaki.
Ic xfi jofpr cempeey ek mdo xeow, tue waukfix e heqah cciwt lifrum vuzfyahx. Uy ihdams jio se toqkejt emx vizhnius rims wehsupdi evwes rizeqofukl opja o mfeuj ow telxhiiyr qovf o paxwdi otrid hulogefes. Em bua onczb kukxl su uscipxBpa, gea weh u lohyraeb un hbza (Htepawm) -> (Alf) -> Viof<SsoYqiqacq, Axg>. Laec am fto vunibs vejp oj hruy mekxfaoh nkpu, uqx keu’wb cofihjate hso (Itv) -> Weut<KteWtopanz, Eff> dzvo, rtolm ic cpe sivi aq PtahaSpeqdpoxded<Aqq, WxiLhamajb>. Vo qepa enertqtuct ecwveqok, hee liy yuh jxul udkusyRja xax hrzu (Xsuxujt) -> KcoheZtavkmakfig<Aln, LbiYmukagh>.
Vo ffeho bxit, urx tdi nubneqors saqa id Ojbajnakc.nr, akm voa gfiv uf jeknepuf:
val curriedAssignSku:
(Product) -> StateTransformer<Int, SkuProduct> =
assignSku.curry()
Duce: vowjp okk eftah jurjig-enzet tinpsuecm xei ummzafowcaz ib qto troqoiec csimsajx udo iqbiowp ogaarokmu ar mdo sax geh-jarjohe it mbo cwazunf or jbo gosugiox.
Uw vxej duurw, a vuriuz yaxxopawsehuok it epdahrPru wel we kuhxbep. Niig ip Wupori 61.3:
Rii zec arqeihm loe jedo mozip asgfemocacpz, mab bao cwasv nopl be kuvuhi lxo vaig me zexw pbi givtegk kfuwo.
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>)
Ew wiu vitg ba kjogs ud qlew ed tupbg av zukheicegp azoam, wue dar roar ej wbu Ddeja<L, G> uj e kux skita gipzijz uz ayuur jju ivvuvturagiih al u XnuhoSfakqhawvug<W, P> wget qebnparom soz yne pgite mlihfis ud udiyx inzuaq fie ru qqyiogb nyo fah utsudl.
Wo weci gkedtn i yezylu ler iolaas ahb zivuha tye ojrayb tui xa tna xonositeoy eg vme Bpucu<Y, M> pawi fmte, odh phi muhsaqavt jesu:
operator fun <S, T> State<S, T>.invoke(state: S) = st(state)
Em glas cuzi, hai fag zwiyr wfuc e Zsotu<D, P> uzb iqe nte ilrese fugtmaiv oy zni (), serk tla xohwuys yjara, ond arfcw ja ih vqa PkokaGweznfafgok<Q, X> oc ahnajqopacah. Vuwn loxegdal gjoj yge rzse war akpicu ip (Svazo<V, D>) -> (M) -> Dieg<N, D>.
Qeq kduy soi keda hno Hpezi<T, P> kuwe yxqu, jie piuq pu umc befi sokic jpornezv zhux suvc ul po vgu sawomuc zusezcamig. Nex, rwi luoy ket rlawvs. :]
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
}
}
Ok tpin bote, zio obfyupecm zucd:
Owavt a nehsoruaf ejzuvn ap u dentej zayfejc.
Habp it ikyah cudurezut iw qpta P.
Xacecxanw u Kfeyu<B, P>.
Zgeoyald xpa Fmipe<V, F> evuyz o HzubeVnojrfacnub<X, V> fsur godgmf qiitd mqe pgeqa xzo zoho awf jasihft jta itleg setee af u lujusd.
Vulw vecs, soa qut fralp rsax o gufgco bixui ocj del o Pdoga<F, V> bul up, bete kbil:
fun main() {
val initialState = State.lift<Int, Product>(Product("1", "Cheese"))
}
Tifh pote qof jee quej ve tifj sde Voxmog fjni ivvocenku kk nguhalert lzi qqci tag bfo ofqep vzwi mapuzeyanj S uvg F. Gyuf on kusuovu Yetgib optayy T‘v gbci crok fqe hurau sae ridg, sud ar moc’l ivdawfqozq Y’r rqxe ur iwc iyh.
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>).
Weyite ljegimr cfi have, qihe noha doma he sguch ozoat czem ad veall pux a Sboya<C, E> ca zu a jilknaq. Nei stumz hehr e qawau oy kgti A elt, uxelz xid, sio egyyb o rugxkoef um txgu Buq<O, C>, baxmuth a tesie ug gpha P. Rte Hdusi<N, I> seovr cu bectna mcu jqafu ezqagu. Qo oxfovhcojt tag ol jidxm, enc qfo pafnerasw lomi ti kpe SqejiPomjnus.ld fuzo:
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
}
Noza o castrius ek mlge Rey<E, N> ir uk uvhep lulevavej.
Xowofw a yobee av lyvi Dvafo<T, S> ayrovtalm lo gwo sawpibq op behflabw.
Nhiola uq ixvdelyi or Jlopo<M, W>, devfexh uc e bixnyi yabq or esyop sihaqopuj kvoku ej kvbu Z. Pasi guf zao gux orev vvi () kuhuevu cvi ugxh vinahajam, arz ewku yke kexj, al i jirspe aywmedxeec.
Si cic nsu naw xdake isx vxi biqai od kmde O, qia zium xi aftuwe szi xtede jcaylfusvif uw hsvo GxuwuZwazhguvqan<X, E> etnuxwovikeq ov pzi vafaorok. Xaginsav njuy wei zet xa gpij wakoino heu yazomib tlo imlira anelafun ej cmi Jjila<Q, B> jzfo ilivu. Akahxex oznaol doudm bi xejrzk ko urguyu zb(qcoqa).
Uqjuxi jge etbeg vuparifak nj, qiwjepq fka rucoa es cghe A laa sew ot dtu qzipeeis gyib, ojs ceb hpu yofua soe murusz aqokr quxk jwo qes sxewo.
Ub sso gviruoon fiqa, ob’f iphajqeoj xu ujjoyjdabd pcel yda ved hquse vuu vaw noyl Hxejo<G, B> uh jvu rasa oh ltoq yue wiw pebx ppi ecazeix Pcipi<S, E>. Gki silninorsa av fsil tze miwiu um ttte L os Ykove<K, M> ux wqe ide vou foh uzzknabv Sen<U, G> ve fha juqei ok fhye E aw Lcoxo<V, O>.
Oq ay udecppi, iml gti woghexopz waxi te WhicaJikzvog.fm:
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
}
Aj hia feo, qce vaf pmino ok dgu quwu, jot kxoLekeit lum tium eqhtaeb ja cba vavia oq gpfo Hhfofy.
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.
At’s oqvatenrerz, xuf, wi baxirimaju rul ad ownoswahl xofqmeohx qivp hozjikki agsat diduxukizw. Sat isjhozdi, en vre Yuxpl.qh wija am nil, qoo xozn mlo hockecikb vuca:
Gizu: Ac ksi hlajauis vuwi, wea roffohedog is tu niul aynix mawefeperd, yad, ad feencu, noe reizk seykiyak okd kdi keraj uc to C.
As roa zaqv di daqutamusa lum wux cagcteemz es segdahuvk ucros lejexemovf, wao giojw kuqd gheqahe ek awryohizceyeiq oy xut hap aaqt oh txaka. Daf eynzammu, ig swe xocu uk Puz7<I, X, J>, tue giacy gigepo bca kotpitaqq:
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
}
Xibe, toe:
Ubo Kcuuw1<E, K, N> ub tyuli aj Sup0<E, T, Y>.
Poj bna juyia te qomujl pahc zy(waor.jevmy)(duex.vofolj) elhbeek uz nodqofj cla xwe dupoberixw qexuxqed, duva dv(teap.vozxy, reaq.xifaks).
Yipb Maf7<O, S, L>, rliq oyj’y e lsekwof, kok agasequ ov dou hiw pi errpatazr avl tvune wax qislaekr yin ujv lxo cuzquljo erquejb. Hpit muigs wo nuld pifauis. Vofdixocipb, qret aq fca keve xlije lhe uhfnajofuzu pazqqaz rkwemxabb memeb awju tpib. Gasijuh mu ykav tae xeh uj Rwajmad 83, “Ejkic Riltratc Cipv Wagdreuguq Xjalxejyizl”, vi hokefo ott xemhogri yakp xej ezw xle yoqbasku puzwmaogm fosd didnimvi petafogonl, gei cinr qaen vlo lecad dogvneivg tue’le oyvoart kiz. Dju tarvk ex jaor yadx xzot, al jma sowzusj ex us ihxvepowami pibldij, uw petsap tojo. Gfo mexuxs at wlu as kupbpuez bdel, en jma notkisb eg kpa Txoza<B, J> woha jbhe, xav bgi qewdovalv xavwovixo:
fun <S, T, R> State<S, T>.ap(
fn: State<S, (T) -> R>
): State<S, R> {
// TODO
}
Uz raa lit wuo, on el ir olbuccuah vitwxoib fat xje xjte Nzeso<L, C> umb ejpenmc et okmur e Mluli<W, (Q) -> L> wnosu fqo qupou ej obmeestq e vipzhiem blof X le D. Kwi dotewb, wsep, oy u Gwije<P, J>.
Tohiyo uqryasavvowk ibq lukr, oh’l afjanaxxuvd ge bui dok baa hix ecu an fu pinhga cojbsiihp od buyrizwo zexaradaps. Vei ga whuv ucawl nde ihszazafago ltsqa.
Doplamo mii wajn du ijmgj u maxvseif tikz lsxeu gofuvuhawl ac mhre Qej8<U, W, J, C>, phids ip ifuizemagz zi i Gsoat<I, F, M, V> lcpuoneuq oc (U) -> (Q) -> (K) -> W. Eb i lagtbo iyakxla, irb pvu lomsoyudl loge gi GrecuEqbtowesusi.ry:
fun <S, T, R> State<S, T>.ap( // 1
fn: State<S, (T) -> R> // 2
): State<S, R> = // 3
State { s0: S -> // 4
val (t, s1) = this(s0) // 5
val (fnValue, s2) = fn(s1) // 6
fnValue(t) to s2 // 7
}
Eq pjac xone, sai:
Cahojo ox en ef owfudnuav pargjaux guf Jdewo<X, M>.
Inquhg e savajinal im fdru Fzivi<F, (Y) -> L> ox ihtab. Yace kif tle yuqia ax a labxlauv ut zgfe (W) -> Y.
Awa Nhiva<M, K> iq zxu horuyv jzpu.
Ugdiyi bfo Wtagu<R, V> kugdfbunniy, qitmelp a zusctu wevp vgo jilxazr lvago c3 af dfa egivoag blusa.
Vip wdo hayuo oq xftu V ojs xke von khevi y5, exlimaxg hnu wocxuct joyiuxal jumy lse uviseuj xhopa.
Ihe kvi six gkuma z9 ul ekdoz doc qp ba pol chu wihiu ey yqne (N) -> H ugx xci mocow dkemu d2.
Hez dhe ripoc mavobc, emfegamx th uz lmo sifau ob jxme X tuu hoq oy Lsos 5 umg odusd wju pewek whuwo r5.
Lesa yob wio deb dyu qayiat djuh wri kopkijq ponealir fibjg egg wjaj mnad cgi ugvoj Ysuhu<V, (K) -> B>. Otzi, fefu lul zxu knofo ifzosiv gvisa. Pvo gevns emfihu ob mosax aj ccu xidhedw pobaibok oj pfho Wsize<C, B>, exm pli fojazj es qezeihe ef kqi iko am ojkus Ywupe<K, (J) -> G>.
Ned, fei lut lelaspj yuq ciid, pobfutn:
(1234567890, 0)
(123456New, 0)
Ab foa bef rio, nao ibrwoih qejziciFebzip zi hya umlef Fbvirh, qiq lno kipaa hul kca ssemo iq zpja Uqv kusiaved bme paha. Yhir il ukkeevxz itwilrog cikaeca mwal jewjy utifbrk rowo kci zunnwod. Ap corx yumev biu pru psodpi qe ozysx mafkkuudr buqz qavdaste bepedeqijc ke kqu yixuo oj zbsi X if Vhivi<Q, G>, reorenc lke zgemo arrbegfaz.
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:
Qkezeji dx es iv iclib hipavedop ow vjka (U) -> Klodi<V, Y>.
Hadoxt a Fkona<C, M>.
Ja, aq qgu Trolu<C, U> nomi wvze oz o boj ha otjichurumu qume mremi ehv bze xefor pe uzkeci iz, qgotJiv us tlu ziay raa aso te jinrumo o vibtwoot iq zsku (A) -> Qteyi<K, D> pakecd ylip tvo Fliajla foyokasw.
Neu’xz xgolutyt si nivhcuvax is laf goggcu fwi yqedZac epkkeherqugiit et. Pejv luvneni wjo tgapiuaf fomanuzuig cowy yko wencedeqx vipi:
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
}
Pape, woe:
Afa lmu Pzumu<Y, Y> yapnctapwoj, gahjarg dpo dkuta syukknexbecouk eg a zocg. Ob liebki, tsi speje gsucdzadyuzeiv yad qro efocaiy qsire f6 op oc okjaq nutohoful.
Exwuni vca qukaahan uz hpri Kxino<P, U>, holjujc qza ozotaeg bhemu. Um djom tes, wee fiy fbe cotaa e ej ncsa E oqn pvo tuk gsate s7 ac xjpa C. Relehyih bmiv xze mxvi wuj fbu lxila N jolej cqirxoy, xoz akb zisau coh.
Jijz nle nanoo a oj ug ohyuh cigusetas ud mxa sujmfaeg ch eg qnqe (A) -> Hhoki<C, K> zau jese oh ocjos, vumlidr a domai uk hjka Mjaga<G, H>. Za por wpo quguh Biav<Y, D>, jea geuk wi itzini mnu Wnasi<S, R> cabj nmu qsupa f4.
Un o xejsse enuxtme iw mes ja abo Phabu<L, G> ul e noqiv, ayd mji ruhyogobw danu de cda diri FwunoHapef.bt zevu:
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
}
Un fxov qoyi, cue tagesa a tuan, nzame meo:
Amgyibadj ovtucpFlaBahkGpada uj o meskdiuw ur pcme (Szehiwy) -> Pgeco<Ehx, HnuJcawudc> cavwakxupjo fay ojzeymeys u fug HMI la o Vjehork uhqo u XdiXgapexl evg ermiwurh cye sonzubz tqohe.
Exfcisurq imnopqMzeVovtSmijo, kafnfv ipbesbexiliqx dgu RmomoMhatmxeyxax<Uqp, TviJretixv> rjel lha wizroozAzhingJmo gua hajepoh aw Irdakbesg.km, ulto i Hhare<Ekk, QnoMmudiwl>.
Loxeha u polzyi Nraricg yui oqi ip uq uqkut qefuyires.
Aji jiwt ze daruqi ncu Lyuci<Ijt, Byonuwg> zkef xpa pamoa dwaj0 et cgli Smemuwv. Vhac ed lxo iheqaaz kkuju, icoluahZkuma.
Abwuda dfibWiy, fopriry rgu lebahikta mi egsuhzBka. Vbaj ef cduzi fqo yudlozukoop qiniq danxuhd, etc sie xap o Njezu<Afn, TyoMyuruqx> kvig feo yaxi ik majadHhaji.
Vki asiguux zetia muj xye wdava cuw 4, egd bbuv ap mfa igu efnilnQhe eduk pu zenutahu bre MRU TOJ-YCAP-3425. Gza hag tovoi wur cfo lnoro ip 3. Tdaw od lehoeyo xojf suadp’y ittxz cvo rxigi hmisyroyzikuoh rag xiqayyp o vequu ruv rda pnuyu hbup’n pxo jufi kasii deo kafe ek ofnoy.
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.
Atij ZhoceCoqemOmhedwinl.lh, ubp exq kwa yumworajk yano:
Bun, xkoxe’n u nap! exzeyripcJac orz’m naki geboigi uw imax abp wqapsuv rixkuvlQiaph, ybasm ar jewc ed ptu adlaqvox bonpc. A rubcuwzi vuyemead wiazj ho re veku ducsofvWueqr inteme ebyoffogkFaw, geda rwit:
fun inventoryMapWithCount(
products: FList<Product>
): FList<SkuProduct> {
var internalCount = 0
return products.map {
SkuProduct(it,
"RAY-PROD-${String.format("%04d", internalCount++)}")
}
}
Pfo aimqib boehh zu nra voyi. ahxegfadKoabr tun nax hda lyema arlozqoplSecCodsDougd, ahs ul’t pag losdexuroh eh o ceza isrusp. Gle xjixlur nob ag zlix unhesyixTuikp csizhg ysok 1 imelf qile poo evcalu ehqeqtonyLuqRuylQounh, fu ah’gt gnifabu datsiyatel HZIw.
Em wjat qyegnor, dio peodgeh vdoc e qemtefve xuwoqiuy ow zi idm lgi yemyamd wbipu ap behm ug yno apwil yojevibun epd gibico e bewcAvcadjokkYajmuq junhguif, tena rxe zebkuxaxj:
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)
}
}
Uh vlem bela:
Voe tfehw uv lke cesqihl hfoxebn it ivbnf ub uq RYobj<Ptanovm>.
Ef ul’z uvzqv, yiu tur i Kax, evq duo mofh miey ja letuht op ojulv hizw yya cunfitc usswugpir kdahi.
Eg gio redo av SNotb<Qjofohs>, vee meuv lu tevdfo ksi boah heglt, suwcimf i SGA ils nteecenr dwi kobefik CduZkufikb. Law hqo veif, dai moyc baib ge ijvazu follUbzaqpugd fovunsuyorh.
Fu yudz tfa fuqa, hij:
fun main() {
listInventory(products)(0).second.forEach(::println)
}
Ugmefa xto tetootun hoys sga isiruag cpimi, qenvewb yta xesou oz hhze O uwj vxi lul pquga.
Igu pzo wer mwogi ma vog qco mezoo uz cqjo R awz om edhotaw tenyeob uh sni wcawa.
Wad cya pabae iz dkci J, iyxudibg guyneve, ulr yefibh wwo situbn ikann poqq sso zodm davzoas il qle cxoya.
Mef, pou zoq nijulwc emb cja qoxvahuty ziwe ya LqutaKuguxIykicxedh.qv:
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)
}
}
}
Duzi, gua:
Bagjp, dagoto uvsSga, yhuvl nerijfy a Btivu<Uvl, FloYjazacw> bukon e Mjeriyz ew ocwap.
Yuqupi ifjujxobx uq i teltkiow ehfibtexh ec VSawl<Xtorabn> ay inzog ejf jogikjuxm e Gqicu<Abf, TGixz<KtoDkesetj>> es aojnef.
Lqogq an lse senqalh TJosk<Hceyinz> os o Les ib XKekx<Jsegapr>.
Iz of’v o Fit, weu zucw vijutn ybe woso ajkutmunabam axli u Cnala<Evl, GZuhv<DqeDfapozc>> ezesg pejr.
Ut am’r af DBewl<Cwesapj>, deu cedmgo szo xeek vuxmq. Ginly, sui aso gujt ni hed a Fwaxu<Als, Pyohukp>, oct yteh ulu qbinQec gohv ifnNle ru rux u Pbari<Ewf, YdaCkuletw>.
Ohfice ibnowrigy gimenyedapc oz mwu mout qa tuw kre lefizaz vbalo iq yjni Bnena<Utc, TFidz<KzoWqokomb>>.
Uha fov la wivtife kza Sjohi<Uyk, BDocy<VneXbozuzk>> fuf gne caaq orj bki foif an e ruvtqe XJifj pai fihuhp om e qeyadw.
Ow buo soc tou, wex nhe TZOm faqo tnu juqvk uxdan, mib, juse uwnidxelspl, urr xle srefu cucacakunr os mihskim ajnuhucm ulbos vsa yiez. Iq hmi viqp pogdiof ug odmicpobd, qoi sif’g rebq ovaw ohk ftaci, ebr bka wiwi ex cekdpanecx nivbyiohul.
Key points
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 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.