A Computational Understanding of Classical (Co)Recursion
Total Page:16
File Type:pdf, Size:1020Kb
A Computational Understanding of Classical (Co)Recursion Paul Downen Zena M. Ariola University of Oregon University of Oregon Eugene, Oregon Eugene, Oregon [email protected] [email protected] Abstract The goal of this paper is to introduce the basic principles Recursion and induction are mature, well-understood top- of coinductive and inductive structures from a computational ics in programming. Yet their duals, corecursion and co- point of view, by presenting a language that captures known induction, are still exotic and underdeveloped programming intuitions about them. For example, Harper [23] explains that features. We aim to put them on equal footing by giving a an element in a infinite stream of natural numbers can only foundation for corecursion based on computation, analogous be generated after computing all the proceeding elements. to the original computational foundation of recursion. At the Moreover, the introduction of stream objects is sometimes lower level, we show how the connection between the two described as the “dual” to the elimination of natural numbers _ can be strengthened through their implementation details [35, 37], but how is this so? Especially since, in the -calculus, in an abstract machine. At the higher level, we develop a stream objects are introduced with an internal “seed” used syntactic equational theory for inductive and coinductive to generate the elements in order, whereas the elimination reasoning based on control flow. We also observe the impact of natural numbers has no such internal state. of evaluation strategy: call-by-name has efficient recursion To make these intuitions precise, we express the (co)- and strong coinductive reasoning, but call-by-value has effi- inductive principles with symmetric forms of recursion and cient corecursion and strong inductive reasoning. corecursion in a programming language. This symmetry is en- capsulated by the duality [9, 22] between data types—defined CCS Concepts: • Theory of computation ! Program by the structure of objects—and codata types—defined by the schemes. behavior of objects. We aim to demystify coinductive codata Keywords: Corecursion, Coinduction, Control, Duality types, and to make them more suitable for widespread use in programming environments [15, 21]. ACM Reference Format: Our contributions (•) and observations (–) are: Paul Downen and Zena M. Ariola. 2020. A Computational Under- standing of Classical (Co)Recursion. In 22nd International Sympo- – (Section2) We point out the impact of evaluation strat- sium on Principles and Practice of Declarative Programming (PPDP ’20), egy on recursion combinators for inductive types: September 8–10, 2020, Bologna, Italy. ACM, New York, NY, USA, * In call-by-value, recursion is eager, and is just as 14 pages. https://doi.org/10.1145/3414080.3414086 inefficient as its encoding in terms of the iterator. * In call-by-name, recursion may end early; an asymp- 1 Introduction totic complexity improvement. Induction is a familiar and commonplace notion with many – (Section3) In an abstract machine, the recursor for firm foundations: logical, algebraic, and computational. Co- inductive types like numbers accumulates a continu- induction—the dual to induction—instead looks unfamiliar, ation during evaluation, maintaining the progress of and is usually relegated to coalgebras [36], since traditionally recursion that is implicit in the _-calculus. only the categorical setting speaks clearly about this duality. • (Section4) Applying duality in an abstract machine based on the sequent calculus [4, 11], we derive the Permission to make digital or hard copies of all or part of this work for corresponding corecursors for infinite streams with a personal or classroom use is granted without fee provided that copies value accumulator dual to the recursor’s continuation. are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation on the first page. Copyrights – (Section 4.2) Like recursion, corecursion can be more for components of this work owned by others than the author(s) must efficient than coiteration by letting corecursive pro- be honored. Abstracting with credit is permitted. To copy otherwise, or cesses stop early. Dual to recursion, this improvement republish, to post on servers or to redistribute to lists, requires prior specific in algorithmic complexity is only seen in call-by-value. permission and/or a fee. Request permissions from [email protected]. • (Section5) We translate the fully-general corecursor PPDP ’20, September 8–10, 2020, Bologna, Italy from the abstract machine back into direct style in © 2020 Copyright held by the owner/author(s). Publication rights licensed _` to ACM. terms of a -calculus using first-class control effects. ACM ISBN 978-1-4503-8821-4/20/09...$15.00 • (Section 5.1) (Co)recursive introduction rules require https://doi.org/10.1145/3414080.3414086 an internal state to express non-trivial computations. PPDP ’20, September 8–10, 2020, Bologna, Italy P. Downen and Z.M. Ariola Var 퐴, 퐵 ::= 퐴 ! 퐵 j nat Γ,G : 퐴 ` G : 퐴 ", # G j _G." j "# j j " j " ::= zero succ rec as ralt Γ,G : 퐴 ` " : 퐵 Γ ` " : 퐴 ! 퐵 Γ ` # : 퐴 # G ~." !퐼 !퐸 ralt ::= fzero ! j succ ! g Γ ` _G." : 퐴 ! 퐵 Γ ` "# : 퐵 ` " : nat Figure 1. System T: _-calculus with numbers and recursion. nat퐼 Γ nat퐼 Γ ` zero : nat zero Γ ` succ " : nat succ Γ ` " : nat Γ ` # : 퐴 Γ,G : nat,~ : 퐴 ` # 0 : 퐴 nat퐸 Γ ` rec " as fzero ! # j succ G ! ~.# 0g : 퐴 In contrast, recursive elimination rules (as in System T) do not. We define the corresponding stateless corecursor Figure 2. Type system of System T. as coelimination. Call-by-name values (+ ) and evaluation contexts (퐸): • (Section6) We present a direct-style equational the- +,, " 퐸 퐸 # 퐸 ory of natural numbers and streams, corresponding ::= ::= □ j j rec as ralt to weak induction and coinduction, based on control Call-by-value values (+ ) and evaluation contexts (퐸): flow. The theory does not resort to bisimulation [18]; +,, ::= G j _G." j zero j succ+ it uses syntactic, locally criteria for valid applications 퐸 ::= □ j 퐸 # j + 퐸 j succ 퐸 j rec 퐸 as ralt of the coinductive hypothesis. 0 • (Section 6.3) We identify the impact of evaluation strat- Operational rules, where ralt = fzero !# j succ G!~.# g: egy on equational reasoning in order to generalize the ¹V!º ¹_G."º + 7! " »+ /G¼ weak (co)inductive principles to strong (co)induction: ¹Vzeroº rec zero as ralt 7! # * In call-by-value, strong induction is sound. 0 ¹Vsuccº rec succ+ as ralt 7! ¹_~.# »+ /G¼º ¹rec+ as raltº * In call-by-name, strong coinduction is sound, which subsumes the traditional notion of bisimulation. Figure 3. Operational semantics of System T. 2 Recursion in the Lambda Calculus These macro-expansions are analogous to the common syn- tactic sugar of let-bindings in terms of functions: The prototypical example of computational recursion is Gödel’s System T [20], whose syntax is given in fig.1. System T ex- let G = " in # := ¹_G.# º " tends the simply-typed _-calculus, whose focus is on func- tions of type 퐴 ! 퐵, with ways to construct and use natural The type system of System T is given in fig.2. The Var, !퐼 numbers of type nat. Values of nat are built with the famil- and !퐸 typing rules are from the simply typed _-calculus. iar constructors zero (denoting the number 0) and succ " The two nat퐼 introduction rules give the types of the con- (denoting the successor of the number represented by "). structors of nat, and the nat퐸 elimination rule types the nat This way, the number = can be written as succ= zero. To use recursor. If rec is seen instead as a primitive function, rather these numbers, System T includes a recursor of the form than a syntactic construct, it would have the type rec " as fzero ! # j succ G ! ~.# 0g. This recursor is rec퐴 : nat ! 퐴 ! ¹nat ! 퐴 ! 퐴º ! 퐴 similar to pattern-matching in functional programming: the nat term " is analyzed to determine if it has the shape zero or whereas the primitive functions corresponding to case and succ G, and the matching branch is returned. But in addition iter on nat would have the type to binding the predecessor of " to G in the succ G branch, this 퐴 퐴 퐴 퐴 recursor also binds ~ to the recursive result that is returned casenat : nat ! ! ¹nat ! º ! " 퐴 퐴 퐴 퐴 퐴 if is replaced with its predecessor. iternat : nat ! ! ¹ ! º ! Notice how the recursor performs two jobs at the same time: finding the predecessor of a natural numbers as well System T’s call-by-name and -value operational seman- as calculating the recursive result given for the predeces- tics are given in fig.3. Both of these evaluation strategies V sor. These two functionalities are captured separately by a share operational rules of the same form, with ! being V _ V V conventional case-expression and the iterator, respectively. the well-known rule of the -calculus and zero and succ Both can be expressed in terms of macro-expansions into defining recursion on the two nat constructors. The only the recursor by ignoring the unneeded parameter: difference between call-by-value and -name evaluation lies in their notion of values + (i.e., those terms which can be substituted for variables) and evaluation contexts (i.e., the case " of f zero ! # rec " as f zero ! # 0 := 0 location of the next reduction step to perform). Note that we j succ G ! # g j succ G ! .# g take this notion seriously, and never substitute a non-value " # " # iter of f zero ! rec as f zero ! for a variable. As such, the Vsucc rule does not substitute the 0 := 0 j succ ! ~. # g j succ ! ~. # g recursive computation rec+ as ralt for ~, since it might not A Computational Understanding of Classical (Co)Recursion PPDP ’20, September 8–10, 2020, Bologna, Italy be a value (in call-by-value).