arXiv:2108.07389v2 [cs.PL] 22 Aug 2021 edmntaeacnrt mlmnaino hssse nt in system this of implementation concrete a demonstrate We 01AscainfrCmuigMachinery. Computing for Association 2021 © a rtietfidwt h eeomn fLs nte17sa 1970s the in Lisp of development the with identified first was utb ooe.Asrcigwt rdti emte.T permitted. is credit with Abstracting honored. be must ACM h uagpolmrfr otedffiut soitdwt co with associated difficulty the to refers problem funarg The Format: Reference ACM C SN978-1-4503-XXXX-X/18/06...$15.00 ISBN ACM iomn skona h uagpolm h uagproblem funarg The problem. funarg the as stack-ba known a is in vironment closures storing with associated difficulty The uie ucinlratv rgamn agae hc i which language, programming reactive functional Juniper nteha.Teeaetovrat fti rbe:uwrsa upwards problem: this of variants two are allocate There availab to heap. is not the solution on are standard The which . scope lexical de own lexical it’s identifiers function’s to nest parent refers the of function in bodies nested the the in where arises functions problem The fo environments. languages based programming in functions first-class of lation embedde functional, memory, heap, stack, closure, funarg, KEYWORDS • CONCEPTS CCS disc formally literature. been the haven’t in but problem funarg the solve l that programming other devices in Arduino present solutions limited other discuss resource also extremely on run to signed stack-alloca be to closures allows pren that the F to System extension simple of a fragment present We me cause fragmentation. may ory allocation heap where resources hav memory often limited systems embedded the However, as memory. systems of computing abundance most an for problem stac a be not o can is heap, This closures the cated. when on determine to closures analysis allocate static solut to apply modern is languages The most then. by since taken attention much received hasn’t INTRODUCTION 1 https://doi.org/10.1145/1122445.112245 Online. pages. 5 2021, USA, 01–03, NY, September with Languages, Problem Functional Funarg of the Solving 2021. In Types. Aksoy. Static Fırat and Helbling Caleb https://doi.org/10.1145/1122445.1122456 F 2,Spebr0–3 01 Online 2021, 01–03, September [email protected] ’21, from IFL permissions requ Request lists, fee. to a redistribute to and/or or servers on post to work publish, this of components n for this Copyrights bear page. copies first that the and on thi tion advantage of commercial ar part copies or that or profit provided all for fee of without granted copies is hard use or classroom digital make to Permission ABSTRACT otaeadisengineering its and Software ; F 2:AMSmoimo mlmnainadApplication and Implementation on Symposium ACM ’21: IFL ucinllanguages Functional etLfyte nin,USA Indiana, Lafayette, West ovn h uagPolmwt ttcTypes Static with Problem Funarg the Solving [email protected] udeUniversity Purdue ae Helbling Caleb → . rcdrs ucin and functions Procedures, rspirseicpermission specific prior ires o aeo distributed or made not e g. oyohrie rre- or otherwise, copy o okfrproa or personal for work s tc n h ulcita- full the and otice we yohr than others by owned C,NwYork, New ACM, 6 closures anguages stack- r e en- sed allo- k ussed fined de- s ein le We . mpi- eis re to r ted. ion nd nd he m- ed ex e edsrb h olwn contributions: following the describe We ems epi idta,a ona h ucinrtrs any returns, function the as soon as that, mind in keep must We lo rgamn agae al tepst ov h fun the solve to attempts Early language. programming Algol ese osletefnr rbe ahrta nlzn clo analyzing than rather problem funarg the solve to seek we exis since problem, funarg the solving to helpful not is This ad uagpolmrfr otemngmn ftesakwh stack the of management the to refers problem funarg wards n yai cpn,lc fsai ye n ual variab mutable and types static of inc lack factors, scoping, of dynamic number a ing by hampered be for to problem appear [10] the problem analyzed further [9] Moses in . must and a structure of data consist based stack a have cannot in calculus closures function that showed and calculus, lambda using . Contributions 1.1 perspective. environmental theoretic type purely a from alloc sens the be in must unique is and contribution values our Therefore boxed heap. the as on treated typically existen are an tials by determined is closures type treat to whose prefer environments, papers plicit These 6–8]. [1, exercise new a oetal aeannepycoue,weestedown the whereas closure), (t non-empty function a another have returns function potentially manage- a may the when stack to the refers of problem ment funarg upwards The downwards. o ecpe ontesakwtotcuigsakcorruptio stack causing without stack itsel the closure down the copied behavior, this be to not Due dynamic. the closure making the variables, deallocati local with capture stack arises may function issue program’s returning an the problem, lex of funarg closure’s top upwards the the the copying to variables) by (captured solved scope easily is problem downwar funarg The problem. funarg downwards the than difficult more a have also may (that closure). function empty another to passed is function a e)adcmoe(pad n onad uagpolm to naturally. problem) and concisely funarg very downwards written and be (upwards compose pr funarg and lan- (downwards map lem) our as T such make functions scopes. can order lexical higher we their lows type, and closures record st over structural polymorphic lexic other a closure’s guage any as a of were viewed type it be the if can Since data. as of function, piece sized calling cally the proble into funarg upwards copied the be from c closure at the size allows closure’s This a time. determining upward statically the by solve problem we narg approach, our ha In problem) solved. funarg adequately upwards been the particular, (in problem narg oa aibe ilb otwe hyaeppe fftestack the off popped are they when lost be will variables local ept h ilso hs al aes ecnedta the that contend we papers, early these of titles the Despite closur function of analysis an made [12] Weizenbaum 1968, In ovn h pad uagpolmi osdrdt emuch be to considered is problem funarg upwards the Solving nlzn lsrsfo yetertcprpciei not is perspective theoretic type a from closures Analyzing • eetne oeal tc loae closures. allocated can stack F enable System to extended of be fragment prenex the how demonstrate We fi[email protected] sablUniversity Istanbul sabl Turkey Istanbul, ıa Aksoy Fırat lscope al ompile lambda ieof size i al- his that e sex- as can- f n A on. les. sures not s For . to m non- ated . fu- s fact lud- ten- ical tial. arg the hat ati- ob- fu- en es n. ds a - IFL ’21, September 01–03, 2021, Online Helbling and Aksoy

environment along with the lambda’s argument for further evalu- Expressions ation. Figure 1 presents the big-step semantics for our language. 4 := G | _G : .4 | 41 42 | ΛU.4 | 4 = Δ( ) Type Schemes E G Δ [EVAL-VAR] f :=  |∀0.f ⊢ G ⇓ E

Types , , := U |  −  →  | X Δ ⊢ _G : .4 ⇓ (_G.4)[{~ 7→ Δ(~) | ~ ∈ fv(_G : .4)}] [EVAL-ABS] Lexical Scope Types X := {G :  , ..., G= : = } 0 0 ′ ′ Δ1 ⊢ 41 ⇓ (_G.43)[Δ2] Δ1 ⊢ 42 ⇓ E Δ2, G 7→ E ⊢ 43 ⇓ E Values Δ1 ⊢ 41 42 ⇓ E E := (_G.4)[Δ] | Λ.4 [EVAL-APP]

Contexts Γ ::= ∅ | Γ,U | Γ, G :  [EVAL-TABS] Δ ⊢ ΛU.4 ⇓ Λ.4 Environments Δ := {G 7→ E , ..., G= 7→ E= } Δ ⊢ 41 ⇓ Λ.42 Δ ⊢ 42 ⇓ E 0 0 [EVAL-TAPP] Δ ⊢ 41  ⇓ E Table 1: Grammar Figure 1: Big step semantics

• We discuss a concrete implementation of this approach in 2.2 Typing the Juniper . Figure 2 presents the typing rules for our language. There are sev- • We analyze the limitations of the extension and identify the eral changes made to the type rules for the prenex fragment of Sys- connection between types and closures. tem F. Of primary interest is the T-ABS rule which also constructs • We make an analysis of other approaches found in exist- the type of the lexical scope. The construction of the lexical scope ing programming languages, but haven’t been discussed for- itself is performed by T-DELTA, which finds all free variables within mally in the literature. the lambda, looks up their type in Γ and returns the corresponding lexical scope type. The rule T-TABS is also changed to restrict type 2 EXTENDING SYSTEM F abstractions to contain no free variables themselves which simpli- In this section, we restrict and extend System F to enable stack allo- fies the type system and assists in compilation to a stack based cated closures. We restrict System F to it’s prenex fragment which environment. separates regular System F types into type schemes and types. This is a necessary restriction to prevent the emergence of existential 2.3 Examples types in the type system which prevents determining the sizes of In this section, we will look at two examples that demonstrate that types at compile-time. The primary addition is that of the closure our solution for the works for accurately typing types, which are defined as function types with their lexical scopes two of the most commonly used functions in functional program- attached to them. The lexical scopes X are either type variables ming: map and compose. For the map example, we will assume or records of captured variables and their respective types. These that the type system is enriched with a list type constructor. The lexical scope types are are considered equivalent if both the field type of these functions are considerably more noisy than the stan- names and the types of those fields are identical. These additions dard System F types. Fortunately, most of the noise can be elided make it possible for closures to be polymorphic and enable stack through type inference. allocation of the closure since the size of the closure is known stati- cally at compile-time. Table 1 defines the grammar of our language. <0? : ∀U.∀V.∀X.(U−X → V)−{} → U list−{5 : U−X → V}→ V list 2.1 Big-step semantics For the compose example we give the definition of compose as Stack allocation of closures require the lambda values and their lex- well as its type in our System F extension. ical scopes to be propagated and used in pairs. For this reason, we combine the lambda values with their environments into a single 2>B4 := ΛU.ΛV.ΛW.ΛX1 .ΛX2. value representation. When a lambda is applied, a flat representa- tion of the lambda’s lexical scope is constructed and used as the _5 : (V − X1 → W)._6 : (U − X2 → V)._G : U.5 (6 G) Solving the Funarg Problem with Static Types IFL ’21, September 01–03, 2021, Online

G :  ∈ Γ [T-VAR] Γ ⊢ G :  true : ∀U.∀V.U −{}→ V −{C : U}→ U (4)

Γ, G :  ⊢ 4 :  Γ ⊢ _G : .4 X false := ΛU.ΛV._C : U._5 : V.5 (5) [T-ABS] Γ ⊢ _G : .4 :  − X →  false : ∀U.∀V.U −{}→ V −{}→ V (6) Γ ⊢ 41 :  − X →  Γ ⊢ 42 :  [T-APP] Applying the cond operator with true and false in equations Γ ⊢ 41 42 :  7 and 8 reveals why Church-encoding does not suffer from the branching problem. In System F, the branch chosen by cond is Γ,U ⊢ 4 : f fv(4) = ∅ already present in the type system by selecting the type for W. Fun- [T-TABS] Γ ⊢ ΛU.4 : ∀U.f damentally, this occurs because true and false have different type signatures, that also determine the branch taken. Γ ⊢ 4 : ∀U.f [T-TAPP] ((condUVU {C : 0}) C 5 (true U V )) : U (7) Γ ⊢ 4 : f[U := ]

((condU V V {}) C 5 (false U V )) : V (8) ~8 ∈ fv(_G : .4) Γ ⊢ ~0 : 0, ..., Γ ⊢ ~= : = [T-DELTA] However, in a language with sum types and pattern matching Γ ⊢ _G : .4 {~0 : 0, ..., ~= : =} (as Boolean types are in most languages), the branching problem with closures will be present. Whether or not this is a major is- Figure 2: Typing rules. sue is debatable, however, we can say for certain that this removes some generality from our approach. An interesting approach to recovering this generality is a paper on open closures by Scherer and Hoffmann [11], however their approach only applies to simply typed and not for System F. 2>B4 : ∀U.∀V.∀W.∀X .∀X . 1 2 The second limitation appears under the presence of mutually (V − X1 → W)−{}→(U − X2 → V)−{5 : V − X1 → W}→ U recursive functions. When compiling top level functions, we can −{5 : V − X1 → W,6 : U − X2 → V}→ W consider their closure to be empty since we can refer to them us- ing a simple function pointer. For inner functions that are often 3 LIMITATIONS AND EXTENSIONS mutually recursive, the situation is more complicated. The lexical The most obvious limitation of the extension presented in this pa- scope of all functions declared in a let-rec blockcan be constructed per is that all branches in a program that return a closure must have by taking the union of all of the lexical scopes of all the functions the exact same lexical scope. One would think that this would be a declared in the block. This constructs a common lexical scope that problem since in most languages, branches of conditionals may re- can be passed to all the related functions. Therefore, all functions turn lambdas with different lexical scopes, as long as the types are declared in the let-rec block will have identical lexical scope types. equivalent. A close analysis of the cond function in System F when When one function in the let-rec block calls another function combined with Church-encoded Boolean values true and false re- in the same let-rec block, it simply passes the lexical scope that it veals a surprising result, this problem simply does not appear in was passed. Within the lexical scope type itself, we place all the the extension described in this paper. functions within the let-rec block with the non-closure function To further elucidate our point, we define cond operator as a type  → . This indicates that this function should be passed the function which takes in a true branch and a false branch and a lexical scope that this function is contained within. Church-encoded Boolean value (as described in equations 3, 4, 5, Extending the system presented in this paper to the impredica- and 6) and returns the respective branch in relation to it’s Boolean tive System F at first glance seems feasible. We believe that the input. funarg problem is orthogonal issue to that of first-class polymor- phism and stack allocated existential types. However we have yet to make a full analysis of how the lexical environments presented cond := ΛU.ΛV.ΛW.ΛX here interact with System F. One possible issue that may crop up _C : U._5 : V._2 : (U −{}→ V − X → W).2 C 5 (1) is subtle interactions with existential types, which can be encoded in System F [2].

4 IMPLEMENTATION cond : ∀U.∀V.∀W.∀X. We have implemented the closure system given here in Juniper U −{} → V −{C : U}→(U −{} → V −X → W)−{C : U, 5 : V}→ W [3], a functional reactive programming language for the Arduino. (2) The Juniper language transpiles all of its code to C++. The analy- sis of C++ in section 5 reveals the issues facing the use of lambdas true := ΛU.ΛV._C : U._5 : V.C (3) as a transpile target. To work around the issues presented in that IFL ’21, September 01–03, 2021, Online Helbling and Aksoy

section, we implement our own function class. This class is con- 5 EXISTING APPROACHES structed by passing in a lexical scope, along with a function pointer. In this section we will analyze a number of different existing lan- In C++, lambda types that do not capture anything can be casted guages and how they solve the funarg problem. To our knowledge to type safe function pointers. We provide our own stack allocated the system presented in this paper is the only language that will closure support by passing in the lexical scope as the first argu- allow the type of a closure to be fully written out with text (ie, it ment to the underlying lambda function. Within this lambda, the does not generate an implicit type or class). inserts variable assignments which extracts the contents In C++, each declaration of a lambda implicitly declares a new of the lexical scope into local variables. Lambdas that do not cap- class [4] and therefore type in the system. Since these classes are ture anything are not passed a lexical scope. auto-generated, it is impossible to write their types using text, and programmers must rely on the auto keyword. These C++ lambdas 1 template 2 class function ; a class with a operator() defined. The problems begin however as 3 soon as you start passing around or returning lambdas. Higher or- 4 template der functions that consume lambdas must be made generic over all 5 class function { 6 private : of these implicit lambda classes. This makes it is impossible to add 7 Result(∗F)(Args...); any sort of constraints on the types of the arguments of the lambda, 8 or the return type of the lambda. Returning a lambda also becomes 9 public : 10 function(Result(∗f)(Args...)) : F(f) {} very annoying, and the auto keyword must be used as the return 11 type. Once again, we see that it is impossible to constrain the re- 12 Result operator ()(Args... args) { 13 return F(args...) ; turn type to anything in particular. The solution used by most C++ 14 } programmers is to make use of the standard library std::function, 15 }; which turns the stack allocated closure into a heap allocated entity. 16 17 template type safety to the use of these implicit lambda classes. 18 class function { To our knowledge, the only other language that has similar ca- 19 private : 20 ClosureType Closure; pabilities to what is presented in this paper is Rust [5]. Just as in 21 Result(∗F)(ClosureType&, Args...) ; C++, the Rust system operates by generating a unique type for ev- 22 23 public : ery closure. However Rust goes a step further with its trait system. 24 function(ClosureType closure , Result(∗f)(ClosureType Like in C++, accepting a closure as a parameter to another func- &, Args...)) : Closure(closure), F(f) {} tion requires the use of a single generic parameter. Unlike C++, the 25 26 Result operator ()(Args... args) { type of this parameter can be constrained with traits. Additionally, 27 return F(Closure, args...) ; returning a closure requires the textual use of one of these traits. 28 } 29 }; 6 FUTURE WORK A lexical scope struct is defined for every combination of envi- In the future we would like to further extend our type system for ronment names discovered in the program. The types of the vari- enriched languages. We are interested in how adding conditionals ables are made generic, so that these lexical scope structs can be and sum types to the language will force changes in the handling declared before any of the other types in the program. Below is of the lexical scopes. We also would like to give a more thorough an example of the lexical scope struct generated by the Juniper description of how to handle the let-rec case. compiler for the compose function. These structs are created and We would also like to extend this system for the impredicative passed to the function constructor the moment the lambda de- System F without restrictions as much as possible, which we pre- clared. This ensures that the closure contains a snapshot of the dict to potentially force additional changes in the representation current environment. Importantly, if a mutation of any local vari- of the lexical scopes. ables occurs after a closure has been constructed, the closure will not be updated (since it essentially takes a snapshot of the local environment the moment it is created). For purely functional lan- 7 CONCLUSION guages this is not an issue since variables cannot be mutated. In this paper we have described an extension to the prenex frag- ment of System F that solves the funarg problem. Our solution 1 // Compose lexical scope solves both the downwards and the upwards funarg problem and 2 template 3 struct closuret_0 { allows closures to be completely stack allocated. Functional lan- 4 T1f; guages can take advantage of this new system to optimize mem- 5 T2g; ory usage, and this extension is particularly useful on embedded 6 7 closuret_0(T1 init_f, T2 init_g) : systems. We also discuss extensions to our language and identify 8 f(init_f), g(init_g) {} the possibility to extend this work to solve the conditional prob- 9 }; lem as well as stack allocation for first-class polymorphism. We discussed how existing languages solve the funarg problem, and Solving the Funarg Problem with Static Types IFL ’21, September 01–03, 2021, Online conclude that our approach is the first so far that allows writing Principles of programming languages - POPL ’96 (St. Petersburg Beach, Florida, closure type signatures without auto-generated types or classes. United States). ACM Press, New York, New York, USA. [7] Greg Morrisett and Robert Harper. 1998. Typed closure conversion for recursively-defined functions. Electron. Notes Theor. Comput. Sci. 10 (1998), 230– REFERENCES 241. [1] William J Bowman and Amal Ahmed. 2018. Typed closure conversion for the [8] Greg Morrisett, David Walker, Karl Crary, and Neal Glew. 1999. From system F Calculus of Constructions. (Aug. 2018). arXiv:1808.04006 [cs.PL] to typed assembly language. ACM Trans. Program. Lang. Syst. 21, 3 (May 1999), [2] Jean-Yves Girard, Paul Taylor, and Yves Lafont. 1989. Proofs and types. Vol. 7. 527–568. Cambridge university press Cambridge. [9] Joel Moses. 1970. The function of FUNCTION in LISP or why the FUNARG [3] Caleb Helbling and Samuel Z Guyer. 2016. Juniper: a functional reactive pro- problem should be called the environment problem. SIGSAM Bull. 15 (July 1970), gramming language for the Arduino. In Proceedings of the 4th International Work- 13–27. shop on Functional Art, Music, Modelling, and Design. 8–16. [10] Erik Sandewall. 1971. A proposed solution to the FUNARG problem. SIGSAM [4] ISO. 2017. ISO/IEC 14882:2017 Information technology — Programming languages Bull. 17 (Jan. 1971), 29–42. — C++ (fifth ed.). 1605 pages. https://www.iso.org/standard/68564.html [11] GabrielSchererandJanHoffmann. 2013. TrackingData-Flow with Open Closure [5] Nicholas D Matsakis and Felix S Klock II. 2014. The rust language. In ACM Types. In Logic for Programming, Artificial Intelligence, and Reasoning. Springer SIGAda Ada Letters, Vol. 34. ACM, 103–104. Berlin Heidelberg, 710–726. [6] Yasuhiko Minamide, Greg Morrisett, and Robert Harper. 1996. Typed closure [12] Joseph Weizenbaum. 1968. The funarg problem explained. unpublished memo- conversion. In Proceedings of the 23rd ACM SIGPLAN-SIGACT symposium on randum, MIT (1968).