ZU064-05-FPR arrowopt 3May2011 10:35 1 UnderconsiderationforpublicationinJ.FunctionalProgramming Causal Commutative Arrows HaiLiu∗,EricCheng†andPaulHudak DepartmentofComputerScience YaleUniversity NewHaven,CT06520,U.S.A. (e-mail:[email protected],[email protected],[email protected]) Abstract Arrows are a popular form of abstract computation. Being more general than monads, they are morebroadlyapplicable,andinparticularareagoodabstractionforsignalprocessinganddataflow computations. Most notably, arrows form the basis for a domain specific language called Yampa, which has been used in a variety of concrete applications, including animation, robotics, sound synthesis,controlsystems,andgraphicaluserinterfaces. Our primary interest is in better understanding the class of abstract computations captured by Yampa. Unfortunately, arrows are not concrete enough to do this with precision. To remedy this situationweintroducetheconceptofcommutativearrowsthatcaptureanon-interferenceproperty of concurrent computations. We also add an init operator that captures the causal nature of arrow effects,andidentifyitsassociatedlaw. Tostudythisclassofcomputationsinmoredetail,wedefineanextensiontoarrowscalledcausal commutativearrows(CCA),andstudyitsproperties.Ourkeycontributionistheidentificationofa normalformforCCAcalledcausalcommutativenormalform(CCNF).Bydefininganormalization procedurewehavedevelopedanoptimizationstrategythatyieldsdramaticimprovementsinperfor- manceoverconventionalimplementationsofarrows.WehaveimplementedthistechniqueinHaskell, andconductedbenchmarksthatvalidatetheeffectivenessofourapproach.Whencompiledwiththe GlasgowHaskellCompiler(GHC),theoverallmethodologycanresultinsignificantspeed-ups. 1 Introduction Considerthefollowingrecursivemathematicaldefinitionoftheexponentialfunction: (cid:90) t e(t)=1+ e(t)dt 0 InYampa(Hudaketal.,2003),adomain-specificlanguageembeddedinHaskell(Peyton Jonesetal.,2003),wecanwritethisusingarrowsyntax(Paterson,2001)asfollows: exp=proc()→do reclete=1+i i←integral−≺e returnA−≺e ∗ PresentlyatIntelLabs,IntelCorporation. † PresentlyatGoogleInc. ZU064-05-FPR arrowopt 3May2011 10:35 2 HaiLiu,EricChengandPaulHudak EvenforthosenotfamiliarwitharrowsyntaxorHaskell,theclosecorrespondencebetween themathematicsandtheYampaprogramshouldbeclear.Asinmosthigh-levellanguage designs,thisistheprimarymotivationfordevelopingalanguagesuchasYampa:reducing thegapbetweenprogramandspecification. Yampahasbeenusedinavarietyofapplications,includingrobotics(Hudaketal.,2003; Peterson et al., 1999a; Peterson et al., 1999b), sound synthesis (Giorgidze & Nilsson, 2008; Cheng & Hudak, 2009), animation (Hudak et al., 2003), video games (Courtney etal.,2003;Cheong,2005),bio-chemicalprocesses(Hudaketal.,2008),controlsystems (Oertel,2006),andgraphicaluserinterfaces(Courtney&Elliott,2001;Courtney,2004). There are several reasons that we prefer a language design based on arrows over, for example, an approach such as that used in Fran (Elliott & Hudak, 1997). First, arrows are more direct – they convey information about input as well as output, whereas Fran’s inputsareimplicitandglobal.Second,theuseofarrowseliminatesasubtlebutdevastating formofspaceleak,asdescribedin(Liu&Hudak,2007).Third,arrowsintroduceameta- levelofcomputationthataidsinreasoningaboutprogramcorrectness,transformation,and optimization. Butinfact,conventionalarrowsarenotstrongenoughtocapturethefamilyofcomputa- tionsthatweareinterestedin–morelawsareneededtoconstrainthecomputationspace. Unfortunately, more constrained forms of computation – such as monads (Moggi, 1991) andapplicativefunctors(McBride&Paterson,2008)–arenotgeneralenough.Inaddition, therearenotenoughoperators.Inparticular,wefindtheneedforanabstractinitialization operatoranditsassociatedlaws. In this paper we give a precise abstract characterization of a class of arrow computa- tions that we call causal commutative arrows, or just CCA for short. More precisely, the contributionsinthispapercanbesummarizedasfollows: 1. Wedefineanotionofcommutativearrowbyextendingtheconventionalsetofarrow lawstoincludeacommutativitylaw. 2. WedefineanArrowInittypeclasswithaninitoperatorthatcapturestheessenceof causalcomputationandsatisfiesaproductlaw. 3. WedefinearestrictedlanguagecalledCCA,inwhichtheaboveideasaremanifest. Forsucharrowsweestablish: (a) anormalform,and (b) anormalizationprocedure. We achieve this result using only CCA laws, without referring to any concrete se- manticsorimplementation. 4. Wedefineanoptimizationtechniqueforcausalcommutativearrowsthatyieldssub- stantialimprovementsinperformanceoverpreviousattemptstooptimizearrowcom- binatorsandarrowsyntax. 5. Finally,weshowhowtoimplementourideasinGHCtoyieldspeed-ups,incertain casesbyovertwoordersofmagnitude. WebeginthepresentationwithabriefoverviewofarrowsinSection2.Theknowledge- ablereadermayprefertoskipdirectlytoSection3,wherewegivethedefinitionandlaws forCCA.InSection4weshowthatanyCCAprogramcanbetransformedintoauniform representationthatwecallCausalCommutativeNormalForm(CCNF).Weprovethatthe ZU064-05-FPR arrowopt 3May2011 10:35 JournalofFunctionalProgramming 3 normalizationprocedureissound,basedonequationalreasoningusingonlytheCCAlaws. In Section 5 we discuss further optimizations, and in Section 6 their implementations in GHC.WepresentsomebenchmarksshowingtheeffectivenessofourapproachinSection 7.WediscussinSection8possibleextensions,andinSection9relatedwork. 2 AnIntroductiontoArrows Arrows (Hughes, 2000) are a generalization of monads that relax the stringent linearity imposedby monads,while retaininga disciplined styleof composition. Arrowshaveen- joyedawiderangeofapplications,oftenasadomain-specificembeddedlanguage(DSEL (Hudak,1996;Hudak,1998)),includingthemanyYampaapplicationscitedearlier,aswell asparsersandprinters(Jansson&Jeuring,1999),parallelcomputing(Huangetal.,2007), and so on. Arrows also have a theoretical foundation in category theory, where they are stronglyrelatedto(butnotpreciselythesameas)Freydcategories(Atkey,2008;Power& Thielecke,1999). 2.1 ConventionalArrows Like monads, arrows capture a certain class of abstract computations, and offer a way to structureprograms.InHaskellthisisachievedthroughtheArrowtypeclass: classArrowawhere arr ::(b→c)→abc (≫)::abc→acd→abd first ::abc→a(b,d)(c,d) Thecombinatorarrliftsafunctionfrombtoctoa“pure”arrowcomputationfrombtoc, namelyabcwhereaisthearrowtype.Theoutputofapurearrowentirelydependsonthe input(itisanalogoustoreturnintheMonadclass).≫composestwoarrowcomputations by connecting the output of the first to the input of the second (and is analogous to bind >>= in the Monad class). But in addition to composing arrows linearly, it is desirable to composetheminparallel–i.e.toallow“branching”and“merging”ofinputsandoutputs. Thereareseveralwaystodothis,butbysimplydefiningthefirstcombinatorintheArrow class,allothercombinatorscanbedefined.firstconvertsanarrowcomputationtakingone input and one result, into an arrow computation taking two inputs and two results. The originalarrowisappliedtothefirstpartoftheinput,andtheresultbecomesthefirstpart oftheoutput.Thesecondpartoftheinputisfeddirectlytothesecondpartoftheoutput. Othercombinatorscanbedefinedusingthesethreeprimitives.Forexample,thedualof firstcanbedefinedas: second ::(Arrowa)⇒abc→a(d,b)(d,c) secondf =arrswap≫firstf ≫arrswap whereswap(a,b)=(b,a) Parallelcompositioncanbedefinedasasequenceoffirstandsecond: (???) ::(Arrowa)⇒abc→ab0c0→a(b,b0)(c,c0) f???g=firstf ≫secondg ZU064-05-FPR arrowopt 3May2011 10:35 4 HaiLiu,EricChengandPaulHudak leftidentity arrid≫f =f rightidentity f ≫arrid=f associativity (f ≫g)≫h=f ≫(g≫h) composition arr(g.f)=arrf ≫arrg extension first(arrf)=arr(f×id) functor first(f ≫g)=firstf ≫firstg exchange firstf ≫arr(id×g)=arr(id×g)≫firstf unit firstf ≫arrfst=arrfst≫f association first(firstf)≫arrassoc=arrassoc≫firstf whereassoc((a,b),c)=(a,(b,c)) Fig.1. Conventionalarrowlaws lefttightening loop(firsth≫f)=h≫loopf righttightening loop(f ≫firsth)=loopf ≫h sliding loop(f ≫arr(id×k))=loop(arr(id×k)≫f) vanishing loop(loopf)=loop(arrassoc−1≫f ≫arrassoc) superposing second(loopf)=loop(arrassoc≫secondf ≫arrassoc−1) extension loop(arrf)=arr(tracef) wheretracef b=let(c,d)=f (b,d)inc Fig.2. Arrowlooplaws Amereimplementationofthearrowcombinators,ofcourse,doesnotmakeitanarrow –itmustadditionallysatisfyasetofarrowlaws,whichareshowninFigure1. 2.2 LoopingArrows Tomodelrecursion,wecanintroducealoopcombinator(Paterson,2001).Theexponential example given in the introduction requires recursion, as do many applications in signal processing,forexample.InHaskellthiscombinatoriscapturedintheArrowLoopclass: classArrowa⇒ArrowLoopawhere loop::a(b,d)(c,d)→abc A valid instance of this class should satisfy the additional laws shown in Figure 2. This classanditsassociatedlawsarerelatedtothetraceoperatorin(Streetetal.,1996;Hasegawa, 1997),whichwasgeneralizedtoarrowsin(Paterson,2001). Wefindthatarrowsarebestviewedpictorially,especiallyforapplicationssuchassignal processing,wheredomainexpertscommonlydrawsignalflowdiagrams.Figure3shows someofthebasiccombinatorsinthismanner,includingloop. 2.3 ArrowSyntax RecalltheYampadefinitionoftheexponentialfunctiongivenearlier: exp=proc()→do reclete=1+i i←integral−≺e returnA−≺e ZU064-05-FPR arrowopt 3May2011 10:35 JournalofFunctionalProgramming 5 arr ::Arrowa⇒(b→c)→abc (≫)::Arrowa⇒abc→acd→abd first ::Arrowa⇒abc→a(b,d)(c,d) (???)::Arrowa⇒abc→ab0c0→a(b,b0)(c,c0) loop ::Arrowa⇒a(b,d)(c,d)→abc (a) arrf (b) f≫g (c) firstf (d) f???g (e) loopf Fig.3. CommonlyUsedArrowCombinators commutativityfirstf ≫secondg=secondg≫firstf product initi???initj=init(i,j) Fig.4. CausalCommutativeArrowLaws This program is written using arrow syntax, introduced by (Paterson, 2001) and adopted by GHC (the predominant Haskell implementation) because it ameliorates the cumber- somenatureofwritinginthepoint-freestyledemandedbyarrowcombinators.Theabove programisequivalenttothefollowingsugar-freeprogram: exp=fixA(integral≫arr(+1)) wherefixAf =loop(secondf ≫arr(dup.snd)) dupx=(x,x) Althoughmorecumbersome,wewillusethisprogramstyleintheremainderofthepaper, inordertoavoidhavingtoexplainthemeaningofarrowsyntaxinmoredetail. 3 CausalCommutativeArrows In this section we introduce two key extensions to conventional arrows, and demonstrate theirusebyimplementingastreamtransformerinHaskell. First, as mentioned in the introduction, the set of arrow and arrow loop laws is not strongenoughtocapturestreamcomputations.Inparticular,thecommutativitylawshown in Figure 4 establishes a non-interference property for concurrent computations – effects arestillallowed,butthislawguaranteesthatconcurrenteffectscannotinterferewitheach other.Wesaythatanarrowiscommutativeifitsatisfiestheconventionallawsaswellas thiscriticaladditionallaw.Yampaisinfactbasedoncommutativearrows. Second, we note that Yampa has a primitive operator called iPre that is used to inject a delay into a computation; indeed it is the primary effect imposed by the Yampa arrow (Hudak et al., 2003). Similar operators, often called delay, also appear in dataflow pro- ZU064-05-FPR arrowopt 3May2011 10:35 6 HaiLiu,EricChengandPaulHudak newtypeSFab=SF{unSF::a→(b,SFab)} instanceArrowSFwhere arrf =SFh wherehx =(f x,SFh) firstf =SF(hf) wherehf (x,z) =let(y,f0)=unSFf x in((y,z),SF(hf0)) f ≫g=SF(hf g)wherehf gx =let(y,f0)=unSFf x (z,g0)=unSFgy in(z,SF(hf0g0)) instanceArrowLoopSFwhere loopf =SF(hf) wherehf x =let((y,z),f0)=unSFf (x,z) in(y,SF(hf0)) instanceArrowInitSFwhere initi=SF(hi) wherehix =(i,SF(hx)) run ::SFab→[a]→[b] sf run f =gf wheregf (x:xs)=let(y,f0)=unSFf x sf iny:gf0xs Fig.5. CausalStreamTransformer gramming (Wadge & Ashcroft, 1985), stream processing (Stephens, 1997; Thies et al., 2002), and synchronous languages (Caspi et al., 1987; Colac¸o et al., 2004). In all cases, theoperatorintroducesstatefulcomputationintoanotherwisestatelesssetting. Inanefforttomakethisoperationmoreabstract,werenameitinitandcaptureitinthe followingtypeclass: classArrowLoopa⇒ArrowInitawhere init::b→abb Intuitively,theargumenttoinitistheinitialoutput;subsequentoutputisacopyoftheinput tothearrow.Itcapturestheessenceofcausalcomputations,namelythatthecurrentoutput dependsonlyonthecurrentaswellaspreviousinputs.Besidescausality,wemakenoother assumptionsaboutthenatureofthesevalues:theymayormaynotvarywithtime,andthe incrementofchangemaybefiniteorinfinitesimallysmall. More importantly, a valid instance of the ArrowInit class must satisfy the product law showninFigure4.Thislawstatesthattwoinitspairedtogetherareequivalenttooneinitof apair.Hereweusethe???operatorinsteadofitsexpandeddefinitionfirst...≫second... toimplythattheproductlawassumescommutativity. Wewillseeinalatersectionthatinitandtheproductlawarecriticaltoournormalization andoptimizationstrategies.Butinitisalsoimportantinallowingustodefineoperatorsthat were previously taken as domain-specific primitives. In particular, consider the integral operatorusedintheexponentiationexamples.Withinit,wecandefineintegralusingthe Eulerintegrationmethodandafixedglobalstepdtasfollows: integral :: ArrowInita⇒aDoubleDouble integral=loop(arracc≫init0≫arrdup)whereacc(x,i)=i+dt∗x dupx=(x,x) To complete the picture, we give an instance (i.e. an implementation) of CCA that capturesacausalstreamtransformer,asshowninFigure5,where: ZU064-05-FPR arrowopt 3May2011 10:35 JournalofFunctionalProgramming 7 • SF a b is an arrow representing functions (transformers) from streams of type a to streams of type b. It is essentially a recursively defined data type consisting of a function with its continuation, a concept closely related to a form of finite state automatoncalledaMealyMachine(G.H.Mealy,1955).Yampaemploysasimilar implementation,andthesamedatatypewascalledAutoin(Paterson,2001). • SF is declared an instance of type classes Arrow, ArrowLoop and ArrowInit. For example, exp can be instantiated as type exp::SF()Double. These instances obey allofthearrowlaws,includingthetwoadditionallawsthatweintroduced. • run ::SFab→[a]→[b]convertsanSFarrowintoastreamtransformerthatmaps sf aninputstreamoftype[a]toanoutputstreamoftype[b]. Asademonstration,wecansampletheexponentialfunctionatafixedtimeintervalby runningtheexparrowoveranuniforminputstreaminp: dt =0.01 ::Double inp=():inp ::[()] ∗Main>run expinp sf [1.0,1.01,1.0201,1.030301,1.04060401,1.0510100501,... We must stress that the SF type is but one instance of a causal commutative arrow, and alternative implementations such as the synchronous circuit type SeqMap in (Paterson, 2001) and the stream function type (incidentally also called) SF in (Hughes, 2004) also qualify as valid instances. The abstract properties such as normal forms that we develop in the next section are applicable to any of these instances, and thus are more broadly applicable than optimization techniques based on a specific semantic model, such as the oneconsideredin(Caspi&Pouzet,1998). 4 NormalizationofCCA In most implementations, arrow programs carry a run-time overhead, primarily due to the use of a data structure for arrow instances, as well as the extra tupling forced onto function’sargumentsandreturnvalues.Therehavebeenseveralattempts(Hughes,2004; Nilsson,2005)tooptimizearrow-basedprogramsusingarrowlaws,buttheresulthasnot beenentirelysatisfactory.Althoughconventionalarrowandarrowlooplawsofferwaysto combine pure arrows and collapse nested loops, they are not specific enough to target effectful arrows, such as the init combinator. Certain effectful arrows are dynamically optimizedin(Nilsson,2005),buttheyarebasedonsomewhatad-hoclaws,andthereare nonormalforms. Ournewstrategyisbasedonthefollowingratherstrikingobservation:anyCCAprogram canbetransformedintoasingleloopcontainingonepurearrowandoneinitialstatevalue. Moreprecisely,anyCCAprogramcanbenormalizedintoeithertheformarrf or: loop(arrf ≫second(initi)) where f is a pure function and i is an initial state. Note that all the essential arrow com- binators, arr, ≫, second, loop and init, are used exactly once, and therefore all of the overheads (tupling, etc.) associated with multiple occurrences and compositions of the ZU064-05-FPR arrowopt 3May2011 10:35 8 HaiLiu,EricChengandPaulHudak Fig.6. DiagramforloopD (a) Original (b) Optimized Fig.7. Diagramsforexp arrowcombinatorsarecompletelyeliminated.Notsurprisingly,theresultingimprovement inperformanceisratherdramatic,aswewillseelater. FirstwedefineacombinatorcalledloopDthatcanbeviewedassyntacticsugarforthe aboveform: loopD :: ArrowInita⇒d→((b,d)→(c,d))→abc loopDif =loop(f ≫second(initi)) A pictorial view of loopD is given in Figure 6. The second argument to loopD is a pure functionmappingatupleof(b,d)to(c,d),wherethevalueoftyped isinitializedbefore loopingback,andisoftenregardedasaninternalstate. Asaconcreteexample,Figure7(a)isadiagramoftheoriginalexpexamplegivenearlier. In Figure 7(b) we have inlined the definition of integral and applied the optimization strategy. The result is a single loop, where all pure functions can be combined together tominimizearrowimplementationoverheads. Tobemorepreciseastowhatkindofarrowsaresubjecttonormalization,wewantto restrictourdiscussioninthissectiononwell-formedandclosedCCAterms. Definition4.1(CCA) Acausalcommutativearrow(CCA)isavalidinstanceoftheArrowInitclass,whoseterms aredefinablefromonlythefollowingcombinators:arr,first,≫,loopandinit.AllCCAs mustsatisfythetwoCCAlawsgiveninFigure4inadditiontothearrowandarrowloop laws. Definition4.1effectivelymeans: 1. WeareonlyconcernedwithCCAswritteninclosedterms,withoutreferencingarrow definitionsfromtheenvironment(syntacticsugarisstillallowed). 2. Sucharrowsmustnotberecursivelydefinedotherwisenormalizationwouldfailto terminate. 3. Thereisnolambdaabstractionorapplicationatthearrowlevelsothatwecanavoid talkingaboutbetareductionorvariablesubstitutionofarrowterms.Note,however, thatwedoallowallformsoffunctions,includingrecursiveones,tobeliftedbyarr. Definition4.1mayseemtoorestrictiveasitprecludesanyformofmodularityforCCA sincealltermsareclosed.Webelievethisisonlyanecessarysteptoavoidcomplicationsin formalizingCCAproperties,andcancertainlyberelaxedinreal-worldimplementations. ZU064-05-FPR arrowopt 3May2011 10:35 JournalofFunctionalProgramming 9 composition arrf ≫arrg7→arr(g.f) lefttightening arrf ≫loopDig7→loopDi(g.(f×id)) righttightening loopDif ≫arrg7→loopDi((g×id).f) sequencing loopDif ≫loopDjg7→loopD(i,j)(assoc0(juggle0(g×id).(f×id))) extension first(arrf)7→arr(f×id) superposing first(loopDif)7→loopDi(juggle0(f×id)) loop-extension loop(arrf)7→arr(tracef) vanishing loop(loopDif)7→loopDi(trace(juggle0f)) f×g(x,y)=(f x,gy) swap(x,y)=(y,x) assoc ((x,y),z)=(x,(y,z)) tracef x=let(y,z)=f (x,z)iny assoc−1(x,(y,z))=((x,y),z) juggle((x,y),z)=((x,z),y) assoc0f =assoc.f .assoc−1 juggle0f =juggle.f .juggle Fig.8. SingleStepReductionforCCA (NORM1) (NORM2) arr f ⇓arr f loopDi f ⇓loopDi f e ⇓e0 e ⇓e0 e0 ≫e0 7→e 1 1 2 2 1 2 (INIT) (SEQ) initi⇓loopDiswap e ≫e ⇓e 1 2 f ⇓ f0 first f07→e f ⇓ f0 loop f07→e (FIRST) (LOOP) first f ⇓e loop f ⇓e Fig.9. NormalizationofCCA OurintuitionbehindthenormalizationprocessistoextendarrowlooplawstoloopD,so that we only get the loopD form as a result. Formally we define a single step reduction 7→ for CCA as a set of rules in Figure 8, and a normalization procedure in Figure 9. The normalization relation ⇓ can be seen as a big step reduction following an innermost strategy,andisindeedafunction. Notethatsomeofthereductionrulesresemblethearrowlawsofthesamename.How- ever, there are some subtle but important differences: First, unlike the laws, reduction is directed.Second,therulesareextendedtohandleloopDinsteadofloop. Toseehowthisworks,itishelpfultovisualizeafewexamplesofthereductionrulesin Figure 8, as shown in Figure 10. We omit the simpler rules that follow directly from the laws,andonlyshowthosethatinvolveloopD.ThediagramsinFigure10canbeexplained asfollows: (a) lefttightening. Figure 10(a) shows that we can move a pure arrow from a left compositioninsidealoopDarrow.Thisfollowsdirectlyfromthelefttighteninglaw ofloop. (b) righttightening. Figure 10(b) shows that we can move a pure arrow from a right composition inside a loopD arrow so that it fuses with the pure function inside the loopD.Thisfollowsdirectlyfromthelefttighteninglawofloopandthecommuta- tivitylawofCCA. ZU064-05-FPR arrowopt 3May2011 10:35 10 HaiLiu,EricChengandPaulHudak (a) lefttightening (b) righttightening (c) sequencing (d) superposing (e) vanishing Fig.10. IllustrationsofReductionRules (c) sequencing. Figure 10(c) shows that we can combine two loopD arrows into one. In doing so, we have to re-route part of the computation, and make use of product lawtofusetwoinitarrowsintoone.Noticethatthereisalsoare-orderingofapure arrowandaninitarrow,whichisduetothecommutativitylawofCCA. (d) superposing.Figure10(d)showsavariantofthesuperposinglawforloopDusing first instead of second. Instead of an outer parallel composition, the second line simplypassesthroughtheloopDunchangedwithsomere-routing. (f) vanishing.Figure10(e)showsanextensionofthevanishinglawforloopstohandle loopD. Since the outer loop only acts on the pure function, it can be moved inside andcomposedwiththetracefunctionduetotheloopextensionlaw. Lemma4.1(Soundness) ThereductionrulesgiveninFigure8arebothtypeandsemanticspreserving,i.e.,ife7→e0 thene=e0issyntacticallyderivablefromthesetofCCAlaws. Proof:Byequationalreasoningusingarrowlaws.Thecomposition,extensionandloop- extension reduction rules are directly based on the arrow laws with the same name; left andrighttightening,superposingandvanishingreductionrulesfollowthedefinitionof loopD,thecommutativitylawandthearrowlooplawswiththesamename.Theproofof thesequencingruleismoreinvolved,andisgiveninAppendixB. (cid:164) Lemma4.2(Termination) ThenormalizationprocedureforCCAgiveninFigure9terminatesforallCCAs. Proof: By structural induction over all possible definitions of a CCA program. The rules NORM1, NORM2, and INIT are the base case, and SEQ, FIRST, and LOOP cover the inductive case, where the sub arrows (such as e and e in e ≫e , and f in firstf and 1 2 1 2 loopf)arenormalizedinductively.Italsoexplainswhythereareexactly8reductionrules
Description: