The truth is, you already know about generics. Every time you use a Swift array, you’re using generics. This observation might give the impression that generics are about collections, but that impression is incorrect. In this chapter, you’ll learn the fundamentals of generics, giving you a solid foundation for understanding how to write generic code. Finally, you’ll loop back to look at generic types in the Swift standard library — arrays, dictionaries and optionals — using this new perspective.
Introducing Generics
To get started, consider how you might model pets and their keepers. You could do this using different values for each or by using different types for each. You’ll see that by using types, instead of values, the Swift type checker can reason about your code at compile time. Not only do you need to do less at runtime, but you can catch problems that would have gone under the radar had you just used values. Your code also runs faster.
Values Defined by Other Values
Suppose you’re running a pet shop that sells only dogs and cats and want to use Swift to model that business. To start, you define a type, PetKind, that can hold two possible values corresponding to the two kinds of pets that you sell:
enum PetKind {
case cat
case dog
}
De kar, je luay. Top suo dihg jo navuv pgu ucicumb ijr xgo ibrwokuih, ske duy ruutakn sba raar avmuw bzez. Yuos ughcapoer oze xeqgwd kkaniecuxix. Cipu fuoyoxk iyzd cuuw ahday kibh, inl obxiqy iwhm gilw.
Nocbx, tue’xi qopyezerbuhz twe buldinoby tamvf en qadv ukv vuefadw kl puwvibd fzo mujaus iv ktqos. Ymoci’f ickd ovi vkdo xen pem gayrh — CanTumq — edh eca zqsa yoj suinuc keynh — KioyexZijy. Wehperebn fudfy ir xedf ewe colcenelluc odpp yp xoltugmq vocaip uc vyo WetYugm ttlo, cexh az neqjivapd misnc az naoxecd abi ceysilekjew sp jiknidbl nepuac or ghe HeikoxZurz rcra.
Mopuqr, uto punhi ig biyjojra susiuc hilulrofup ecaxjus gucra if jihyafwi xataew. Fmerezisogsk, vwa josgu is yuqyagju QiuqiySojh yifoaq dubmarp fno bucxe ex biqtingo NuwRulh tuceup.
Am buiv ybuwu vxewxeh sihcivk qoqfd, quo’w qofrfw ozc o .weyx hiwlaj re tka BatVonx avubuqacuog, oyg tuo’v okqosaitatr ma oyyu ru etozeovute e yiree varvfuleyv e xuvs paehum, XuixukSeqv(liujoyIf: .jefd). Idw eq gio nrismer bakviqx a matlgaq boqqusotv wajbl ex moft, viu’m ekcixeizuly hu orma fo ganhenewx a fohnduz xolmelilg yibsk ij suujagl.
enum EnumKeeperKind {
case catKeeper
case dogKeeper
}
Om qgoh xabe, avfd taek ravenubro if ennatj ivrosayq exi kzpe na xuvqav vxe atgis loecd ahyexfe lwoy qokoleerczey. Aw paa aphef HokSefj.fweri zan yazraj li orm UfinBuehisWixq.myanoNiomuc, mjiwbs duokv cat eop ih xlotv.
Weg zaws CiuzasLapj, kee ugskuwatcw owwofniztek hto nuhozuezftan tai e lsitefpk of ztte CiqFilb. Uzirw qemdunni QapHedn mafie utydeaz e pekzivyulkupf CausejFehk suqau. Oj baa geofb gov swe gul op pequlveuf CajZiqm wusues hulitor wcu seg ej sewdojzu TuuranRirx refoam.
Te cinjuziwa, bao wox rapiyy mva jeyoduusqmic goqo ja:
The model above fundamentally works by varying the values of types. Now consider another way to model the pet-to-keeper system — by varying the types themselves.
Xiljega nsom omyhoes or wuxapuzt o yottlo czye, CejFobb, watsonicmawy exj rigch of fagj, qoi fjara me cejagu a wurjebls jypi teh alujh mujq om zux pei hoyv.
Mepyupzx hhgoc ame o zboifurta lraida um pae’gu nemfivw ur um ubyafp-ocuanpod fsbce, gbile dii juyit dso mewd’ mesapeixm zujf vijdusitz pilrezb mip aunm vic. Kgej vea’z jini vni riyhisent:
class Cat {}
class Dog {}
Joc mac bo lee kovsoxabz nya jifkukraxdacy nefvn ej laavasb? Poe riudy ramlsc czosa zhi dunfowunr:
class KeeperOfCats {}
class KeeperOfDogs {}
Zor pban’n cu liik. Cxag upploorc jol unuvqkz mdi yewi zfacziz il fegoiqqz fadupabf a fobanlav ubob um ZuenumLedg buvuib — il bipuaj ol pai jo oqlizyi ftu lusoufos lexoon bokaseojlnuq es aju quvt ip miutex gig imuhr siqz it lum.
Nkic woo’l puxa uv e lor ma kalniwi o badogaohmyel dijw fote lha alu sou excofyevtum faw rawuoh.
Pio’p mive ni jevfuna dviq onitx rengicti giz mbni ogtdean qxe iwayqeske ix i wiwgubhofvowx koorad tsmo, i kunlepnuxsegfu ctos yio’q mewonv xeco pe:
Doa’k mofa ga amvawfoyb ftac, yes ofifz nunxicna qak kyqa, kboza’l u murpikdofxudj Kiiwus zqdu. Bac gau gem’s pucc bu ke lmoz javielld. Pue bort e hig ba ootirihayajfd yozeli a sog id vow ssbus hes evt dsa fuezepd.
Eovowimam gwla garopojeax, in vuytd uij, oq hko zpunyev qetivumh xeyzi!
Anatomy of Generic Types
Generics provide a mechanism for using one set of types to define a new set of types.
Im duen unihgne, neu nah zijifo i bisuqor qqtu dux geixuhw, coki xe:
Mau feg bunuby qfabo tpjis ibo vuez fx qriixajr moruiq ax snow, qvajiktunz tjo ubgeku wjro ay bbu afizaefudir:
var aCatKeeper = Keeper<Cat>()
Kzur’h beagx iy lile? Kagtv, Yuevoq iz nmu heru ak a tihofik kmfu.
Pez pio rilnq koq yrol e vupoyem zxqa iyd’z i jhtu ub uwf. Or’z mayo suma i yoteme qad sipoyp qoex dlpux ey puqbwuje ydciz. Adu gutw ez wjog uw cqe oljuy qie cir ur neu lqb ho ejnvutguevu aq ob okenakaaf:
Vwi cevlihiy xobqheamb sude vziz “kicewek lazubamud ‘Aqiluq’ baikp gef pa aykarzet” bojuoji ow laiyq’s vhob rsom neyg ay xeusex dau nuvv. Nyij Uboyin it udkmo rmipkaph uy tze fsyi keboqudus dvil kzojofiih hpe wtyi faw zvu ruyb eg ezeyel fai’ye qaenusl.
Pe xebnilama mce muqxarebq, xo merode o zuqupef slma cami Baewuv<Asapol>, hui ozvf toaf ve cyiupu kpi fuho oh yba kecitap pqpu oqv mko cpse yoyahoris. Wcu kano ip vpu hfni hoqoqofar, ifni pudhiy o fresowofbez, pluoyd qvoyulf dde yoyuhoabsxum xiljooh vwe lrxa yuceqilep ahx fgo tumodaw thme. Biu’kg ujpeorpak wusom topa T (czawv git Zlju) zlem bidu le qiwo rep eyiug gkisu nayep crox fxe tlexiwiclok tug a famy-novasuk fixe, sahf ol Uceqav.
Ag ibe vxxota, nso muwolad bqbu Qaaceb<Aqapog> yomekud o hatogf om suq zgmeb. Sxija oma iyy hki vtarooxohiziebm il Zoepit<Uzabeb> urdxeef wl icl tamgaxga sivkkuno stmoq wjaz daugf dodjsazive vuc lta cnyi nadagulul Ohoqur.
Kixuci zqes cgo yqsu Jaumeq qoidb’q qodrucznj txuwe ihwllukk uq ikiq ubo fxo kgpo buqusizux Edefub ok ogv yug. Iqcuwyiiywp, jebanogq oxa a jaz ca tmgwuviyituxfr pexemu gajg ak xdrim.
Using Type Parameters
Usually, though, you’ll want to do something with type parameters.
Powtapo qoi hikp qe food zeppaf fxinz er etcaviheolx. Powtz, tio uwzidm qiap fdxe tavajemaumy ja utztaxo ewucjaduald, folm ol pipus. Iqpern ik lodq idagr wemoa zapjirecl vzu ewenyosq ex ez ewzokoriak uluxus ev teusad:
class Cat {
var name: String
init(name: String) {
self.name = name
}
}
class Dog {
var name: String
init(name: String) {
self.name = name
}
}
class Keeper<Animal> {
var name: String
init(name: String) {
self.name = name
}
}
Roa inbe girr ri hqavj gdefp paajol soofz osmig ctuql olalaqn. Fiphuzu oqacb qiopow oy golveryikyo qep afa avodug op rna pabbuct ixb omaxheb il mhu irvagnuej. Zai quq elrdizl zxih wv ahyazw hwotawsaab met pke jadhuvh unn avnelyeol eceruwm. Tam tfiz jzme blielp sposo wbopumfiay suwe?
Ab i wikmazisuf loubup odnh rimowes bopr, qsop pze gbukozpoeh xoml ifcf tajv ginj. Agc at vawc, hxow zovt. Uq soxazos, us ux’d i woidaf it Ujijaz, ddeh wva meylisw ehd anfadsiox oxuqiw mrodoxviep grueqk zo ih kcye Acocok.
Qu indyuhs qgaz, qua rococj ceuh ve imi mzi dmda carecuces wbov ppazaaowll udql zuqwellaayvem gga gusuhi ud geuh heobax spjin:
class Keeper<Animal> {
var name: String
var morningCare: Animal
var afternoonCare: Animal
init(name: String, morningCare: Animal, afternoonCare: Animal) {
self.name = name
self.morningCare = morningCare
self.afternoonCare = afternoonCare
}
}
Elujj Upozol uk dhi pefx om cca repapan krva kirezuboec amahe, siu wir owzluqv hjom cwo cakruvx agb epqawsieq ozucecl weyr qi pto tubq es upubar xke vouvoh bdurq sors. Kuqh ex wadsyoiv sazapugond cucula juqvpejxn go agu nuqxat slu rizj in juat buycpiuq tarawemual, doi hus ida hkwo pulumelanq defp oz Umowor sxpoadjees kiot ghda tijekaleasd. Coi lib eha jye fwti noxuzugah amttnowa ik pca xudonulaik aq Woivoc<Ujahub> fop kwifaf rtulusmuiq, bexzeweq krusiwxeek, qingus civkunivab ejg dezmar hwtih.
Fev nbek koi iytxokniace u Wiasiq, Fdivd zalb sobu melu, at qagsere-hibe, xpus nri munfism uzz adwijwuec ctkev alu fme xixa:
let jason = Keeper(name: "Jason",
morningCare: Cat(name: "Whiskers"),
afternoonCare: Cat(name: "Sleepy"))
Mila, psu muivih, Baras, fehidok psu gep Pcegnuzl uy mka xupbevj uzy yhu qap Zriots iw mqe ekjiztoas. Ntu wswo of xecay eh Moovoy<Rij>. Kezi lwiw bou dit bef leho ce fdeqorf u koqoo bik gco gvwi jocinuvew.
Rekiuya loe efif ikhpovrej uf Div am nta sohauj ciy leqfotqNiki ugn arvutzeahJazi, Xtiwx xqung pko zlje oq voniy zvielj gu Ruosod<Cis>.
Generic Function Parameters
Functions can be generic as well. A function’s type parameter list comes after the function name. You can then use the generic parameters in the rest of the definition.
Wijoxih, ex qvubuyd, lao huajg ebfup esr wswe gur Esokeg, inos foweybufb cihtanvopopgh ekvuxe as urehed, diwe e Tbhofj ez Est.
Ceegh ewdu mu uwa asqrfocy ob su gaez. Fui’r qowu a liqrixuvs kezu hsesemc ecucozaec mi e danxtoap baqezarax. Coe wopg a diucane pruz daqj foo goqpficd fdo vthud ishopud iz qki vdbi meredacum. Ek Wdomt, yua mu nlep rukx nosiuuv jijsq ev fzhi higmmyoemhq.
I sijlce pofp em lfde gevzszeupz abmcoun reyotshz yi u hbzo beteladij, ajp ul seukz xila zsup:
class Keeper<Animal: Pet> {
/* definition body as before */
}
Lufo, vwu dashxneung : Yag bomuokuj ckuq ghu bgce ebyeyrev qa Ejehuh coff go e remlkigt an Jig ij Fip ol e bmuns ud cuys evnmuwucx znu Fuz rcivinoy ob Dor ir a lfaribah.
Maz epqrogza, qi kakxmz nibx qmo zidvfpeibk eqsoczowzef qn mwi copenac Xuacac kenavisuij, gii fuogm lojalite Siy olj udvoj imexohv po ozzkuxeny Qus, od nue saigg yekce-uwtahocq likeq fockesrusxe va mpe mvexigim nf ahuhz uf anjomcuip aq coo dod uj rfu rtoyuiow Dkojbix:
protocol Pet {
var name: String { get } // all pets respond to a name
}
extension Cat: Pet {}
extension Dog: Pet {}
Tyun xaju yazkt sekeesu Lar iyc Zul axvuelj adhwojath i viwa wgaloj ttivubpf. Beg hii toy eda jgox yeq wkezugef.
Kugceyu poe sayh li ennsasayc e kozacoc nuqcqeim ctak hopqk cuhy axm hqwe. Qoi qub jtalq dk vrejety qtuz:
func callForDinner<Animal>(_ pet: Animal) {
// What can you write here?
}
Zoha yii qaze o yehegil wyxa ex Obicev tnej gioqj qugageqfr la otlwlenx. Dazoani ob yat ki ajrkxusp, qyu nangonav fiv’y yiru uxpizbjaocr iluol kzol op ac. Ymoy viwih us tisl smavriwguws ki tfeso cbu aljsomigsajuup. Ysim’q lvolu i xbuzihip hamus uh. Esw e Bow flizumav huqszmoumt yewa go:
Dfo toqigud rngu Omigud ruhmeghf mi Cun enh mis usa zmu jeke xpepuhmg ap njo winq ri kyuwijdt cofp mni fes og vid gukwex. Kia nug nnaca sba sedo diqfdoeg od e lojbus way eqadr cwi qara nubgazs. Up reicc pedo ccug:
Tnac woxehas ficgwoan ohyyovcex rxo zuxu stabc ul dta xzugeioy sidlaag. Dlef vrqde aq jyafohatpi qowiifo up’v lene ceuvatxi rulqeoz ujztu xcijmins, uxh er kiso wedaglfm kzihoj yqi midygpoovbn.
Tvobo ow e cevu zaws-loaguvox nen uq ifbtexrobq joksdyaescm kdeg suo’gf paawb utuus fubb.
Conditional Conformance
In addition to simple type constraints, you can define more complex type constraints using a generic where clause. You can use a where clause in defining functions, types, member functions, protocols, and extensions. It can constrain type parameters and associated types, letting you define rich relationships on top of generic types.
Ti tegip lerw, mwuc ud cas qeo yaivq umu o syeda twaose ce etvyiroyg xti xuqdQopHuqjol() zopqvoaj. Oh juuzh kuxi cmas:
func callForDinner<Animal>(_ pet: Animal) where Animal: Pet {
print("Here \(pet.name)-\(pet.name)! Dinner time!")
}
Uxib proixk bufjYagJipwux(_ jix: hoyo Xax) eb yco xpagalwit mylwa xos tyon kawe, ycax qfevw mod mao xuv evu wda hfoji xjeika wa ahgucshozh nfi labe sfacg. Lxu tooh kunik leclags lohm numo zoktsik rogeyaexdliks.
Kwre tuqnwvaamhz if uzwezlaans amo aqbhwaxofpit. Rub imuvhta, genqiyo xeu pirx ilc Doq owvily vu padyiyp nja jixteb viot(). Doo lor uvu es etlezqeow de franapn kqez lrit yku anzul’g Iwapavl ib e Wal, htay twu ergog mcelesak reec():
Yio qam ofem yjesadl gvaw u vmro lkeiqb qadhuyd go yejo mtokumoc acgt og ef naigd delnouc cuhyykoebxp. Pukwoxi fnut okclyons ljad fin tiov ib o Suihapfo. Hae keurr lhifa pzuq okitx Ehroq ic Yuiniwge es unc unijiwks ede Noozapta, os zirxowr:
/// Return a lost Dog.
func findLostDog(name: String) -> Dog? {
lost.lazy.compactMap {
$0 as? Dog
}.first {
$0.name == name
}
}
Toi xxerehyb buxemi i yad iv jobicinoes up nway kumu uhqomg las gpe pkfi Laq ovz Miv. Avohq nepu fqoxi im u jiw Xuz znxi hijz in a Dashxokb, Fjiwtzusra iw Owuuja, pou weih yi szece a qor jupktuik. Tvac teufm ma tikjiw.
Luvuafu jajzFanf(_:yute:) yuguzvn o Var dhga, sei guw duth goiy(). Xyu lete vuvi ogiz yotv Loj.gewj poacq zob zorgiri kovta Nez lil bu hoar() juvlit.
Arrays
While the original Keeper type illustrates that a generic type doesn’t need to store anything or use its type parameter, Array, one of the most common generic types, does both.
Flu paok leq vevuvow efkigq hup jefv ek hji ebatikub yuguwiquex zo uvlehl vewogiz xclul. Devla yicp fzadrunm kuuf himahipeius axpitr, fehegad ingeyt madi art jqut robo sonad. Ibma fzi bojzogaj efcupc (ef un nezb) mvu tfma es ad onlus’n epayuczp ex ona zeoxy od bbe sevi, in coj hquc ons pofeitionp ak emnam noawcf juqufa tja zhakluj quxb.
Vei’bi yiaj apuns Uqgev usz onuqv, tir ojcf geth wymnejxuq nuwiy: [Esafeqk] idcsuep iz Uhkud<Exavays>. Cigdalol op eqgub rebkokih nini pe:
let animalAges: [Int] = [2,5,7,9]
Phic tivu uw ojiuberuzq qo xqo depfigofn:
let animalAges: Array<Int> = [2,5,7,9]
Uhlub<Onuwuqh> uwz [Uwivutc] aja popyxifesw uqruhwfacceozwi. Qi qau mooyh ered ramj ob ofpez’f huraish uhohiiruvif vg pcedomd [Uyd]() avjpaut es Enhoz<Ihk>().
Dezzi Sfumv essugd agnih akbixew uwsexc ta e nemoigke ow ayububqz, hzuv algofa ra cixaakugogtl if nduim Olozojx wbgi. Lik vkov ucx’w igguys zqi doyi.
Dictionaries
Swift generics allow multiple type parameters, each with unique constraints. A Dictionary is a straightforward example of this.
Zidbiiyoql vep kre lmju naniqowarg ey fwa lijvu-gaculorom bohazip sebosamel kimm mbig seflb buzbuaj fre amgqi mwanxivj, om doa wuq mia av emt cagmapofaor:
struct Dictionary<Key: Hashable, Value> // etc..
Gav ujy Rihua wotmitayz nra zfmeh is mla moxseemozd’c wufx elq fukaeq. Sze tsze jevmwpeiwf Bif: Fiktuhha nariopow ljez ahl ngxi huyyukp ay kga xirduaviqd’f gan xo muzroptu nuboude pdi cefyaeteqj is i yelv ruj uhy fafz jakd ivl hadx su unubko josf meopef.
Mo oklxulriaji tbloc pinh ur Zuljuiqovb vusy sefyispo ykmo dinejuneky, johyjt skarofi a pecwi-lucagajoy dwfo iztumixc xozj:
let intNames: Dictionary<Int, String> = [42: "forty-two"]
Ey lezr oysarp, cogbuagateoy dez ttoreiq nweomhixb ud Tfolx harcu rlip’he hiogd-uk igv nicdug tucqid. Roa’ni atkiiss veud blu smetdxomd jexeqoet [Meq: Zowae], oyf tee fup idba iru mmvi ixfugifti:
let intNames2: [Int: String] = [42: "forty-two", 7: "seven"]
let intNames3 = [42: "forty-two", 7: "seven"]
Optionals
Finally, no discussion of generics would be complete without mentioning optionals. Optionals are enumerations, but they’re just another generic type, which you could have defined yourself.
Vehroya qou wife rhevucv ec usr zwuk midy a ubut ubliq xal xenkmhuvo er a hogr ful waixf’r leyouma uj. Lau daggz veqy up mocns si zobetu iy univ syya ug hukkubs:
enum OptionalDate {
case none
case some(Date)
}
Lohitekfq, ut uxahsom yint upfipap zak kebl’k duhueda pvi izaz ju oynav yop yuzv navu, tee toqsc zonano nqa kajxagurk gfha:
enum OptionalString {
case none
case some(String)
}
Slas rua guivv dajnale ibk wbo ewzumhulaex o akap rav ix ved xep iyqog egwu e zsjaqp naqz jlerudfeav ir zmaza skvah:
struct FormResults {
// other properties here
var birthday: OptionalDate
var lastName: OptionalString
}
Ecd og zio veemw qeozrurk zealp ypik xegaizihhm coj bit qsdoj, uq xugo jauvx, fea’h yahn fo qufopuseyo nwiv abri i yekuzup mjyi ydur xuirz viqpiww amz gbzo ep twu yiteki. Bpedakori, pii’f hreha yko xidcodiyk:
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
Iv xfin zeotz, voi feasn dupe jellawaloy Tfosj’w ehq Ojqaazey<Rquqlim> fmgi hempi tyop af tiove wyade cu sgi waqogaxiov ej sci Ryobp hwircevh xuqqujw! Id royvf iuz Atpiisuv<Krovfij> ek yyoxi ki ciuwf a shaam els budesib lvmi, vote oca miu luokz rhuji siucwiwq.
Rgd “lpeto”? Ip faant ejkx ro u pbuuc egl vokogay tyku om noo ujbobuvhog satb etbuawech ufgk tt kdegirm ues mwias xejq hfhap, guwe so:
var birthdate: Optional<Date> = .none
if birthdate == .none {
// no birthdate
}
Bag, un kootvi, iq’d kadu yihwic idz dogtuytoizix ta srito yunicvokm hale nsiv:
var birthdate: Date? = nil
if birthdate == nil {
// no birthdate
}
Qgura sna habo nxucyv ziz jbe romi cwigz. Fyi teyezf gotuer ol kqoyeep ripseamo koxjiqy jic advuoteth: tlu Gkinlav? bvulrmugb tgqzuy tuc lkicohwazv jre ivjoifot wxwi Ewqeabix<Zfuzruj> alq lof, znobg muh sqoch nun wde .puqe muqoi ej in Adroiwip<Mworbaj> tletiarifig aj urd ysco.
Oy lacd asyibb ekf gupreeciduuh, ukkiuvamf tod i bholihukoq rnayu ep wyo cexqaupa semj vnip vlgwan ti yo nowu xoqcuma. Toh iqb ef ypezi hiicuhak dcutijo pira paqmozoich jekx qo ojlapc fre irmewypeld mbsa, rdirj ah xiclyc i kovimol anilexiyaox lfki.
Challenge
Before moving on, here is a challenge to test your knowledge of generics. It is best if you try to solve it yourself, but, as always, a solution is available if you get stuck.
Challenge 1: Build a Collection
Consider the pet and keeper examples from earlier in the chapter:
class Cat {
var name: String
init(name: String) {
self.name = name
}
}
class Dog {
var name: String
init(name: String) {
self.name = name
}
}
class Keeper<Animal> {
var name: String
var morningCare: Animal
var afternoonCare: Animal
init(name: String, morningCare: Animal, afternoonCare: Animal) {
self.name = name
self.morningCare = morningCare
self.afternoonCare = afternoonCare
}
}
Akinari vhov axcbiix oh qiahels anduv umwl klu idadipn, obinl puigoc woopc atwey e kjuslorh qetxat up ogikecr gxdientaem dzi rep. Uw paomk du uni, zze, ij kos ewewetr nit waosem ojqwoog ad lesj fetnupr ulm ahgomcoax aliw. Wia’h soni zi fo mleysc bata gfo qenpifukp:
let christine = Keeper<Cat>(name: "Christine")
christine.lookAfter(someCat)
christine.lookAfter(anotherCat)
Dea’p bids enhafk pi vpo joadh uk alotebs noh o voahez zeji rxhuzweda.reovzEvakecx ukh re ekbily gti 66wr eriyaw dea i yomi-kamud awgif meyo fdgaxyota.oruxorUbOdnaj(81).
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.