Functional Perl: Programming with Recursion Schemes in Python

Functional Perl: Programming with Recursion Schemes in Python

Functional Perl: Programming with Recursion Schemes in Python Robert J. Simmons Nels E. Beckman Dr. Tom Murphy VII, Ph.D. Carnegie Mellon University The tom7.org foundation frjsimmon,[email protected] [email protected] Abstract Algorithms that are fundamentally expressions of structural in- tm1 + x1 tm2 + x2 x1 + x2 = x3 duction over a polynomial data type are famously awkward to tm1 + tm2 + x3 int(x) + x implement in object-oriented programming languages, leading to Using the pattern matching syntax present in the ML family of three-day-bucket hacks like the “Visitor Pattern.” We show that the languages, the implementation of this big-step evaluation semantics exception-handling mechanism present in most object-oriented lan- is similarly straightforward: guages already suffices. Conventional wisdom dictates that exceptions are in essence fun eval tm = about the non-local propagation of errors. Upon close scrutiny, we case tm of find the contrary: exceptions are fundamentally about unchecked Int x => x depth-1 structural pattern matching. | Plus (tm1, tm2) => eval tm1 + eval tm2 We give examples of this programming idiom in Python, Perl, and Java. JavaScript requires a language extension, which we build We can also similarly implement a small step semantics for this and deploy. We then show how two styles by which this style may language as defined by the following rules: be re-integrated into programming languages in the ML family. We conclude by suggesting design changes that would facilitate the use int(x) value of this idiom in other languages. 0 0 tm1 7! tm1 tm1 value tm2 7! tm2 Categories and Subject Descriptors D.2.3 [Coding Tools and tm + tm 7! tm0 + tm tm + tm0 7! tm + tm0 Techniques]: Standards 1 2 1 2 1 2 1 2 x1 + x2 = x3 General Terms Algorithms, Design, Languages, Standardization int(x1) + int(x2) 7! int(x3) Keywords pattern matching, recursion schemes, structural induc- The ML implementation is a straightforward implementation tion, python, perl, java, javascript, types of these inference rules, with the exception of the need to define an additional cast function that we call to get the value of the 1. Introduction enclosed integers once we know the terms to be a values.1 Pattern matching is a convenient way of representing polynomial exception Stuck datatypes, which can be informally thought of as a generalization of tree-like data. An extremely simple example is an abstract syntax fun value tm = for a language with integers and addition: case tm of datatype tm = Int x => true Int of int | _ => false | Plus of tm * tm fun cast tm = The big-step evaluation semantics for this language is quite straight- case tm of forward: Int x => x | _ => raise Stuck Copyright c 2010 by Robert J. Simmons, Nels E. Beckman, and Tom Murphy VII. Permission is hereby granted, free of charge, to any person obtaining a copy of this fun step tm = software and associated documentation files (the ”Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, case tm of publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons Int x => raise Stuck to whom the Software is furnished to do so, subject to the following conditions: | Plus(tm1, tm2) => The above copyright notice and this permission notice shall be included in all copies if value tm1 or substantial portions of the Software. THE SOFTWARE IS PROVIDED ”AS IS”, WITHOUT WARRANTY OF ANY then if value tm2 KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WAR- then Int(cast tm1 + cast tm2) RANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE else Plus(tm, step tm2) AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPY- else Plus(step tm1, tm2) RIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LI- ABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 1 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR In a language without static typing and with distinct varieties of values, THE USE OR OTHER DEALINGS IN THE SOFTWARE. these casts can, of course, fail if we get the wrong variety of value. interface tm class tm: { public <T> T accept(Visitor<T> visitor); } pass class Int implements tm { class Int(tm): int x; def __init__(self, x): public Int(int x) { this.x = x; } self.x = x public <T> T accept(Visitor<T> v) class Plus(tm): { return v.visit(this); } def __init__(self, e1, e2): } self.e1 = e1 class Plus implements tm { self.e2 = e2 tm e1; tm e2; def eval(tm): public Plus(tm e1, tm e2) { try: raise tm this.e1 = e1; except Int: return tm.x this.e2 = e2; except Plus: return eval(tm.e1) + eval(tm.e2) } public <T> T accept(Visitor<T> v) def value(tm): { return v.visit(this); } try: raise tm } except Int: return True except: return False interface Visitor<T> { public T visit(Plus p); def step(tm): public T visit(Int i); try: raise tm } except Plus: if(not value(tm.e1)): public class VisitorExample { return Plus(step(tm.e1), tm.e2) static int eval(tm tm) { elif(not value(tm.e2)): return tm.accept(new Visitor<Integer>(){ return Plus(tm.e1, step(tm.e2)) public Integer visit(Int i) { return i.x; } else: return Int(tm.e1.x + tm.e2.x) public Integer visit(Plus p) { return p.e1.accept(this) ex_1 = Plus(Plus(Plus(Int(1),Int(2)), + p.e2.accept(this); Int(3)),Int(4)) } print ("Evaluating: Plus(Plus(Plus(1,2),3),4) - " }); + str(eval(ex_1))) } print ("Stepping x3: Plus(Plus(Plus(1,2),3),4) - " + str(step(step(step(ex_1))).x)) public static void main(String[] args) { tm ex_1 = Figure 2. Python pattern matching case study. new Plus(new Plus(new Plus(new Int(1), new Int(2)), new Int(3)), new Int(4)); mechanism, which Java also incorporates, is fundamentally about System.out.println unchecked depth-1 structural pattern matching, we can seek to use ("Evaluating: Plus(Plus(Plus(1,2),3),4) - " exception handling to provide a similar sort of depth-1 structural + eval(ex_1)); pattern matching to the Java language and other object-oriented } programming languages. The rest of the paper explores this pos- } sibility. Figure 1. Case study implemented with the Visitor Pattern in Java. 2. Discovery and implementation in Python Imagine we have the abstract syntax of our example encoded in While there is a somewhat more pleasant way to implement the Python in the standard way, as a class hierarchy where subclasses Step function using nested pattern matching, we will only consider Int and Plus extend the basic abstract syntax class tm. the “shallow” pattern matching we see here in this paper. class tm: 1.1 Object-Oriented languages and the Visitor Pattern pass class Int(tm): While ML-family languages suggest describing different types of def __init__(self, x) syntax as different branches of a subtype, most object oriented self.x = x languages suggest describing different types of syntax as different class Plus(tm): objects that either implement the same interface or extend the same def __init__(self, e1, e2) subclass. The traditionally understood way of performing elegant self.e1 = e1 recursion over these structures is known as the Visitor Pattern; the self.e2 = e2 implementation of big-step evaluation using the Visitor Pattern can be seen in Figure 1. The big-step evaluation semantics of this language could be The Visitor Pattern, whatever its merits may be, is certainly written by explicitly using isinstance tests, but the repeated use quite different in character from the depth-1 pattern matching used of elif isinstance(tm, ...) is bulky and unsatisfying, which in our ML example. However, because the exception handling only becomes more true when the code has many, many branches: def eval(tm): class tm extends RuntimeException {} if isinstance(tm, Int): final class Int extends tm { return tm.x final Integer x; elif isinstance(tm, Plus): Int(Integer x) { this.x = x; } return eval(tm.e1) + eval(tm.e2) } else: raise RuntimeError final class Plus extends tm { final tm e1; Note the final line of the above code, which raises a runtime final tm e2; error if the evaluated term is neither an Int nor a Plus. This is Plus(tm e1, tm e2) { important in case we extend the abstract syntax tree with more this.e1 = e1; branches; we want something equivalent to the “nonexhaustive this.e2 = e2; match exception” in an ML-family language. } } 2.1 Utilizing the exception handling mechanism public class PatternMatch { The first key observation for introducing our idiom is the fact that every single object in Python can be treated as an exception static Integer eval(tm tm) { and thrown; the language designers suggest that exceptions derive try{throw tm;} from the Exception class, but there is no enforcement of this catch(Int i) { return i.x; } mechanism. The second key observation is that the branches of catch(Plus p) an exception handling call can check the object’s class tag when { return eval(p.e1) + eval(p.e2); } deciding which branch of the exception handling to consider. The } motivation is to allow, say, a divide-by-zero error or a user-input error to be both caught at the same place but handled in different static Boolean value(tm tm) { ways; we can use it, however, to do exactly the isinstance test try{throw tm;} from before in a more concise, pleasing notation: catch(Int i) { return true; } catch(Plus p) { return false; } def eval(tm): } try: raise tm except Int: return tm.x static tm step(tm tm) { except Plus: return eval(tm.e1) + eval(tm.e2) try{throw tm;} catch(Plus p) { Furthermore, this form of evaluation introduces a natural notion if( !value(p.e1) ) of a nonexhaustive match exception: if we add new kinds of terms return new Plus(step(p.e1), p.e2); (say, a multiplication) and don’t handle them in the eval function, else if( !value(p.e2) ) the very abstract syntax tree node that we did not handle will be return new Plus(p.e1, step(p.e2)); thrown as an exception: an error that potentially contains much else more helpful information than an ML Match exception! return new Int ( ((Int)p.e1).x + ((Int)p.e2).x ); 2.2 Adaptation in Java } } The relatively-more-strongly-typed language Java also allows for the expression of our idiom by simply allowing every term to ex- public static void main(String[] args) { tend the class RuntimeException.

View Full Text

Details

  • File Type
    pdf
  • Upload Time
    -
  • Content Languages
    English
  • Upload User
    Anonymous/Not logged-in
  • File Pages
    6 Page
  • File Size
    -

Download

Channel Download Status
Express Download Enable

Copyright

We respect the copyrights and intellectual property rights of all users. All uploaded documents are either original works of the uploader or authorized works of the rightful owners.

  • Not to be reproduced or distributed without explicit permission.
  • Not used for commercial purposes outside of approved use cases.
  • Not used to infringe on the rights of the original creators.
  • If you believe any content infringes your copyright, please contact us immediately.

Support

For help with questions, suggestions, or problems, please contact us