Design Concepts in Programming Languages Scheme Supplement

Size: px
Start display at page:

Download "Design Concepts in Programming Languages Scheme Supplement"

Transcription

1 Design Concepts in Programming Languages Scheme Supplement Franklyn Turbak and David Gifford with Mark Sheldon Copyright c by Franklyn Turbak, David Gifford, and Mark Sheldon Version created on September 6, 2005 at 15:35. This is a draft. Please do not cite. Send all bugs, feedback, etc. to fturbak@wellesley.edu. Draft September 6, 2005

2 Draft September 6, 2005

3 Contents 1 Definitional Interpreters The Definitional Interpreter Game PostFix Interpreters An Operational Style Interpreter for PostFix A Denotational Style Interpreter for PostFix An Embedded Interpreter for PostFix Summary Definitional Translators The Definitional Translator Game Simple PostFix Translators SM: A Stack Machine Language A PostFix to SM Translator Program Simplification Motivation Simple SM Transformations An SM Simplifier A General Simplifier An Interpreter for FL An Interpreter for FL Syntactic and Semantic Datatypes The Evaluator Environments Delayed Values Handling Primitives Parsing Desugaring A Read-eval-print Loop i

4 Contents A A Scheme+ Tutorial 1 A.1 Introduction to Scheme A.1.1 Values A.1.2 Definitions A.1.3 Recursive Procedures A.1.4 Quote, Quasiquote A.1.5 Mutation A.1.6 Equality A.2 Datatypes and Pattern Matching A.2.1 Introduction A.2.2 Advanced Features A.2.3 Pseudo-Datatypes A.2.4 with-fail A.2.5 Constructor Details ii

5 Chapter 1 Definitional Interpreters One of the best ways to explore the landscape of an unfamiliar programming language is to express it in terms of a familiar one. This seemingly simple notion embodies two deep ideas in computer science: Writing a program is one of the best ways to clarify thinking about a wide range of concepts. Since programs are just a kind of data, they can be manipulated by other programs. Together, these two ideas suggest using a familiar language (the defining language) to define the semantics of an unfamiliar language (the defined language). There are two fundamental strategies for defining one language in terms of another: interpretation and translation. In the interpretation approach, a program written in the defining language evaluates programs written in the defined language. In the translation approach, programs in the defined language are translated into programs in the defining language. We shall study interpretation in this chapter and translation in the next. Of course, a prerequisite to these strategies is a solid understanding of the defining language. But this understanding needn t be grounded in a formal operational or denotational semantics. Extensive programming in the defining language can build strong, almost visceral, intuitions about computation that are valuable for analyzing other languages. Besides, operational and denotational semantics also require a solid understanding of a defining language the mathematical metalanguage. Programming language notation can be less cryptic than the mathematical metalanguage but powerful enough to express the concepts and techniques of the other semantic frameworks. 1

6 Chapter 1 Definitional Interpreters Best of all, interpretation and translation naturally lead to language definitions that can be executed. This encourages an experimental approach to programming language analysis and synthesis. We can get a better feel for a defined language by executing its programs. We can explore the space of language design by tweaking various parameters of the language definition. We can use interpreters and translators as a testbed for investigating pragmatic issues. In short, defining one language in terms of another can turn a computer into a laboratory workbench equipped with a versatile set of tools for experimenting with programming languages. 1.1 The Definitional Interpreter Game Interpretation is based on the notion of an agent that evaluates programs. We will use the term virtual machine to refer to any agent that accepts programs written in a specified programming language and returns a result. A virtual machine may be a physical computing machine, but it may also be a human being carefully following a set of evaluation rules. We will purposely be vague about the way in which the virtual machine receives its inputs and generates its outputs. The programs and results might be represented by text files, data structures, characters interactively typed and printed on a terminal, handwriting on a piece of paper, or even electrical signals appearing at the pins of a chip. As a pedagogical device for explaining interpretation, we will introduce a graphical notation that suggests the relationship between virtual machines and programs. Here is our representation of a virtual machine: Virtual Machine This representation is reminiscent of a piece from a jigsaw puzzle. Input programs can be plugged into the indentation at the top of the piece, and the results of running the program appear at the protrusion at the bottom of the piece. The indentation and protrusion have different shapes to emphasize that the input and output of the virtual machine may be in different forms. Every program effectively turns one virtual machine into another. Here is a graphical notation for executing the PostFix program ((5 sub) 3 swap exec) on a PostFix virtual machine: 2

7 1.1 The Definitional Interpreter Game ((5 sub) 3 swap exec) PostFix Virtual Machine = -2 Virtual Machine The program is represented as a jigsaw puzzle piece with a hole that can be filled in by a virtual machine with the appropriate shape. The combination of the program and virtual machine to the left of the equals sign is equivalent to the virtual machine to the right of the equals sign. In this case, the resulting virtual machine is one that ignores its input and always returns the numeral -2. The notation indicates that the resulting virtual machine inherits its output from the underlying PostFix virtual machine. It is also intended to emphasize that a program can t do anything until it is combined with a virtual machine. The virtual machine serves as a kind of battery pack that makes the program go. If PostFix supported in and out commands that performed interactive input and output of numbers, then executing the program (in 2 mul out) would create a virtual machine for doubling numbers: (in 2 mul out) PostFix Virtual Machine = Doubling Virtual Machine The indentation at the top of this machine represents the input to the in command, while the protrusion at the bottom of the program represents the output of the out command. The fact that the program entirely encloses the PostFix virtual machine indicates that the program overrides the normal output channel of the PostFix virtual machine by a different mechanism (in this case, out). An interpreter is a program that transforms a virtual machine for a base language B into a virtual machine for an interpreted language I. This relationship is depicted in Figure 1.1. The program is called an I interpreter written in B or sometimes just an I interpreter if the base language B is 3

8 Chapter 1 Definitional Interpreters Interpreter for language I Virtual Machine for language B = Virtual Machine for language I Figure 1.1: The definitional interpreter game board. understood; the term evaluator sometimes appears in place of interpreter. Sometimes the term I interpreter is used to mean virtual machine for I, but we will maintain a distinction between the program that defines a language (the interpreter) and the agent that implements a language (the virtual machine). Interpreters can serve several purposes. Pragmatically, an interpreter is a means of implementing a virtual machine for a programming language. The processor at the heart of modern computers is a virtual machine for a low-level machine language; an interpreter can lift this machine language to a level more amenable to programming. Semantically, an interpreter is a means of defining one language in terms of another. An interpreter is said to be definitional if its primary purpose is to elucidate the meanings of constructs in the interpreted language. While a definitional interpreter can indeed be used to evaluate programs, it is designed to be easy to understand rather than efficient to execute. Nevertheless, a definitional interpreter can serve as a starting point for exploring pragmatic dimensions of a language. 1.2 PostFix Interpreters In this section, we illustrate definitional interpreters by presenting several Post- Fix interpreters. As our base language we will use Scheme+, our extended version of the Scheme dialect of Lisp invented by Gerry Sussman and Guy Lewis Steele Jr. The syntactic simplicity and semantic power of Scheme+ make it an excellent language in which to write interpreters. Scheme+ will serve as our lingua franca for programming throughout this book. The Scheme+ language is Scheme extended with facilities for datatypes and pattern matching. These facilities enable us to express interpreters and translators more succinctly than we could in Scheme alone. Before you pro- 4

9 1.2.1 An Operational Style Interpreter for PostFix ceed, we recommend that you skim the Scheme+ tutorial in Appendix A to familiarize yourself with the language. If you are new to Scheme, or you need a refresher, you should begin with Section A.1. If you are already familiar with Scheme, you need only look at Section A.2, which describes the datatype and pattern matching extensions. We begin by presenting a PostFix interpreter that is based on an operational semantics. For comparison, we then consider a PostFix interpreter with a denotational flavor. We conclude with an interpreter for PostFix2 that demonstrates a powerful way of thinking about programming language syntax An Operational Style Interpreter for PostFix The PostFix interpreter presented here is inspired by the SOS for PostFix presented in Sections?? and??. Figure 1.2 shows how we can encode the structure of an SOS configuration in terms of Scheme+ datatypes. A PostFix program is modeled by the program and command datatypes. The dollar sign prefix on the constructor names in the program and command datatypes is our convention for indicating constructors of syntactic objects. As described in Section A.2, define-datatype is a convenient way to introduce sum-of-product datatypes, so called because each instance is a tuple (product) of the constructor arguments that is tagged to indicate which constructor (summand) created the instance. 1 Thus, a pop command is constructed by the Scheme+ application ($pop), a 3 command is constructed by ($int 3), a mul command is constructed by ($arithop *) 2, and the program ((5 sub) 3 swap exec) is constructed by ($prog (list ($seq (list ($int 5) ($arithop -))) ($int 3) ($swap) ($exec))) The reason for introducing datatypes for PostFix programs and commands is to establish an abstraction barrier between the particular s-expression syntax 1 Only the number of elements in argument specification for a datatype constructor is semantically relevant; the actual elements serve only as comments. For example, int suggests that $int expects an integer argument, (listof command) suggests $prog and $seq expect a single argument that is a list of commands, and (-> (int int) bool) suggests that $relop expects a functional argument that maps two integers to a boolean. 2 Note that arithop and relop take as their arguments not the names of procedures but the procedures themselves. 5

10 Chapter 1 Definitional Interpreters (define-datatype program ($prog (listof command)) (define-datatype command ($int int) ($seq (listof command)) ($pop) ($swap) ($sel) ($exec) ($arithop (-> (int int) int)) ($relop (-> (int int) bool)) ) (define-datatype op-val (int->op-val int) (seq->op-val (listof command))) ) (define-datatype configuration (config (listof command) (listof op-val))) ;; initial-config: (-> (program) configuration) (define (initial-config pgm) (match pgm (($prog cmds) (config cmds ())) )) ;; final-config?: (-> (configuration) bool) (define (final-config? c) (match c ((config () (cons x _)) #t) (_ #f) )) Figure 1.2: Datatypes and auxiliary procedures for the operational style interpreter. 6

11 1.2.1 An Operational Style Interpreter for PostFix that represents a PostFix program and the Scheme programs that manipulate the abstract structure of PostFix programs. For example, both PostFix and PostFix2 (Figure?? on page??) can be parsed into the program and command datatypes. Note that the above program and command constructors resemble the syntax of PostFix2 except that here programs and executable sequences are represented by lists of commands rather than single commands. We shall have more to say about this resemblance in Section The stack component of a configuration is represented as a list of operational values (instances of the op-val datatype), where an operational value is either an integer or an executable sequence. A configuration itself is built by the config constructor, which combines a list of commands representing the program with a list of values representing the stack. The initial-config procedure maps a PostFix program into an initial configuration, while the final-config? predicate determines when a configuration is final. Both initial-config and final-config? use Scheme+ s match construct to unbundle a datatype instance into its component parts. Even though Scheme+ is a dynamically typed language (one in which types are associated with values rather than with syntactic expressions), it is often helpful to talk about the types of expressions, particularly the types of names denoting procedures. For this reason, we will commonly annotate procedure definitions with a comment 3 that indicates their intended type. For example, both initial-config and final-config? are annotated with types written in an s-expression format. Thus, (-> (configuration) bool) indicates that final-config? is a function that takes a configuration as its single argument, and returns a boolean. See Chapter?? for more on types. We will model a single transition in the SOS semantics by a step procedure that maps one configuration to another configuration. The definition of step in Figure 1.3 is an encoding in Scheme+ of the PostFix rewrite rules from Figure??. At a high level, step is a dispatch routine that determines how each kind of command is evaluated. Such a dispatch routine is at the heart of any interpreter. A few notes regarding the definition of step: step makes heavy use of the match construct to unbundle a configuration into its components, to dispatch on the type of the first command, and to express complex constraints on the form of the stack for particular commands. 3 In Scheme+, the semi-colon character ; introduces comment text up to the next carriage return that is ignored by the Scheme+ virtual machine. 7

12 Chapter 1 Definitional Interpreters ;;; step: (-> (configuration) configuration) (define (step cfg) (match cfg ((config (cons cmd cmds) stack) (match cmd ( ($int i) (config cmds (cons (int->op-val i) stack)) ) ( ($seq l) (config cmds (cons (seq->op-val l) stack)) ) ( ($pop) (match stack ((cons _ s) (config cmds s)) (_ (error "POP: empty stack"))) ) ( ($swap) (match stack ((cons v1 (cons v2 s)) (config cmds (cons v2 (cons v1 s)))) (_ (error "SWAP: too few elements"))) ) ( ($sel) (match stack ((cons else (cons _ (cons (int->op-val 0) s))) (config cmds (cons else s))) ((cons _ (cons then (cons (int->op-val _) s))) (config cmds (cons then s))) (_ (error "SEL: inappropriate arguments"))) ) ( ($exec) (match stack ((cons (seq->op-val l) s) (config (append l cmds) s)) (_ (error "EXEC: not a sequence"))) ) ( ($arithop op) (match stack ((cons (int->op-val n1) (cons (int->op-val n2) s)) (if (and (eq? op quotient) (= n1 0)) (error "Divide by zero") (config cmds (cons (int->op-val (op n2 n1)) s)))) (_ (error "ARITHOP: inappropriate arguments"))) ) ( ($relop op) (match stack ((cons (int->op-val n1) (cons (int->op-val n2) s)) (config cmds (cons (int->op-val (if (op n2 n1) 1 0)) s))) (_ (error "RELOP: inappropriate arguments"))) ) )) (_ (error "STEP: no commands")) )) Figure 1.3: Dispatch routine that models 8 a single rewrite step of a PostFix SOS.

13 1.2.1 An Operational Style Interpreter for PostFix Stuck states are modeled by signaling an error via Scheme+ s error facility. In the clauses that handle arithmetic and relational operators, op is a procedure that can be applied to two integers. The clause handling arithmetic operators contains a test to handle a divideby-zero error specially. This test makes use of Scheme+ s ability to test (via eq?) if two procedures are identical objects. quotient is Scheme+ s procedure for integer division. The step procedure models the relation of the PostFix SOS. The final-stack procedure below is patterned after the laststack function from Section??. It iteratively 4 steps its single configuration argument until a final configuration is reached, at which point it returns the final stack: ;;; final-stack: (-> (configuration) (listof op-val)) (define (final-stack cfg) (if (final-config? cfg) (match cfg ((config _ stack) stack)) (final-stack (step cfg)))) To complete the interpreter, we call final-stack from a procedure pf-oper that maps a PostFix program to a final answer (represented as an integer or the symbol executable): ;; pf-oper: (-> (program) (+ int executable)) (define (pf-oper pgm) (match (final-stack (initial-config pgm)) ((cons val _) (match val ((int->op-val i) i) ((seq->op-val _) executable))) (_ (error "Empty final stack")))) Here are a few examples illustrating the use of pf-oper 5 : 4 Even though final-stack is syntactically a recursive procedure, the tail recursive property of Scheme+ guarantees that step executes iteratively i.e., in constant control space. See Section A.1.3 for more details. 5 The notation Scheme+ indicates evaluation by a Scheme+ virtual machine. 9

14 Chapter 1 Definitional Interpreters ; ((5 sub) 3 swap exec) (pf-oper ($prog (list ($seq (list ($int 5) ($arithop -))) ($int 3) ($swap) ($exec)))) Scheme+ -2 ; (1 2 lt 17 (3 mul) sel) (pf-oper ($prog (list ($int 1) ($int 2) ($relop <) ($int 17) ($seq (list ($int 3) ($arithop *))) ($sel)))) Scheme+ 17 ; (1 2 gt 17 (3 mul) sel) (pf-oper ($prog (list ($int 1) ($int 2) ($relop >) ($int 17) ($seq (list ($int 3) ($arithop *))) ($sel)))) Scheme+ executable ; ((5 sub) 17 mul) (pf-oper ($prog (list ($seq (list ($int 5) ($arithop -))) ($int 17) ($arithop *)))) Scheme+ error In the last example, the token error indicates that Scheme+ has signalled an error. Evaluating PostFix programs using the above method is rather tedious. We can greatly improve the interface to the PostFix interpreter with two simple extensions. The first is a parser that translates the s-expression syntax for Post- Fix programs into instances of the syntactic datatypes manipulated by pf-oper. Such a parser is implemented by the procedures pf-program, pf-sequence, and 10

15 1.2.1 An Operational Style Interpreter for PostFix (define (pf-program sexp) (match sexp ((list->sexp lst) ($prog (pf-sequence lst))) (_ (error "Ill-formed program")))) (define (pf-sequence lst) (map pf-command lst)) (define (pf-command sexp) (match sexp ( (int->sexp n) ($int n) ) ( (list->sexp lst) ($seq (pf-sequence lst)) ) ( pop ($pop) ) ( swap ($swap) ) ( exec ($exec) ) ( sel ($sel) ) ;; Below, arithop and relop operations are functions, not symbols! ( add ($arithop +) ) ( sub ($arithop -) ) ( mul ($arithop *) ) ( div ($arithop quotient) ) ; integer division ( lt ($relop <) ) ( eq ($relop =) ) ( gt ($relop >) ) ( _ (error "Unrecognized command") ) )) Figure 1.4: A set of procedures for parsing s-expressions into instances of the syntactic datatypes for PostFix. pf-command in Figure 1.4. The second is a read-eval-print loop that prompts the user for a PostFix program (in s-expression form) and prints out the result of evaluating that program. The pf-repl procedure in Figure 1.5 implements a simple read-eval-print loop. It uses pf-program to parse the program. Here is a transcript of a sample interaction with the read-eval-print loop: 11

16 Chapter 1 Definitional Interpreters (define (pf-repl) (begin (display "\n\npostfix=> ") (let ((sexp (read))) (if (eq? sexp quit) (display "\ngoodbye!\n") (begin (newline) (display (pf-oper (pf-program sexp))) (pf-repl)))))) Figure 1.5: pf-repl implements a simple read-eval-print loop for PostFix. (pf-repl) postfix=> (1 2 add) 3 postfix=> ((2 mul)) executable postfix=> ((swap exec swap exec) (1 sub) swap (2 mul) swap 3 swap exec) 5 postfix=> quit Goodbye! This completes the description of the operational style interpreter for Post- Fix. We emphasize our primary goal in writing this definitional interpreter was to express the essence of the PostFix SOS in as clear and concise a way as possible. Yes, the resulting program can be executed, and yes, there are many ways to improve the efficiency of the interpreter, but these are only secondary concerns. We encourage you to experiment with and modify this interpreter. The exercises suggest many worthwhile improvements and extensions to the interpreter described here. Exercise

17 1.2.1 An Operational Style Interpreter for PostFix a. Extend the PostFix interpreter to handle the dup command from Section?? and the pair command from Exercise??. b. Test your modified interpreter on the factorial and fibonacci command sequences from Exercise??. Exercise 1.2 Extend the PostFix interpreter to handle the for and repeat commands from Exercise??. Exercise 1.3 Extend the PostFix interpreter to handle the I, def, and get commands from Exercise??. Exercise 1.4 Recall that there were two approaches for handling the exec command in the PostFix SOS: the [execute] rule and the pair of rules [exec-progress] and [execdone]. a. To which approach does the $exec clause within step correspond? b. Modify the interpreter to implement the other approach. Exercise 1.5 Modify the PostFix interpreter so that it displays the current command list and the current stack every time it executes a command. (Note: displaying the names of arithmetic and relational commands is tricky.) Exercise 1.6 The interpreter presented above uses Scheme+ s error system to model stuck states in the PostFix SOS. One of the drawbacks of this approach is that it is necessary to restart the PostFix read-eval-print loop every time an error is encountered. This exercise explores two alternate approaches to handling errors that do not share this drawback. a. One approach is to extend the configuration datatype to represent stuck states explicitly: (define-datatype configuration (config (listof command) (listof op-val)) (stuck string)) Modify the interpreter to generate and handle stuck configurations. b. Another approach is to introduce a distinguished error stack, as suggested in Section??. Implement a stack datatype that captures this notion, and modify the interpreter to generate and handle error stacks. 13

18 Chapter 1 Definitional Interpreters Exercise 1.7 a. Implement a stack abstraction whose interface consists of the following procedures: empty-stack: (-> () stack) Return an empty stack. stack-empty?: (-> (stack) bool) Return #t if the stack is empty and #f otherwise. push: (-> (op-val stack) stack) Return the stack resulting from pushing a value onto a stack. pop: (-> (stack) stack) Return the stack resulting from popping the argument stack. Signal an error if the stack is empty. top: (-> (stack) op-val) Return the top value of the stack. Signal an error if the stack is empty. b. Modify the PostFix interpreter so that it uses the stack interface for all stack operations rather than relying on pattern matching. c. Discuss the advantages and disadvantages of using a stack abstraction vs. pattern matching. Exercise 1.8 The interpreter presented above unbundles and bundles configurations at every step of the computation. This exercise demonstrates how to derive a version of the interpreter that avoids all bundling and unbundling operations. Perform the following steps in order: a. First, modify final-stack and step so that they accept the components of a configuration in unbundled form. That is, each should take two arguments a command list and a stack rather than a configuration. step should still return a configuration as before. b. As it stands, the step procedure always returns by calling config on the resulting command list and stack. Modify step so that it takes a third argument, return, that will replace config as the means of returning the resulting command list and stack. Update calls to step to pass config as the third argument. c. At this point, final-stack should now contain a subexpression that looks something like: (match (step cmds stack config) ((config cmds1 stack1) (final-stack cmds1 stack1))) 14

19 1.2.1 An Operational Style Interpreter for PostFix Note that the transformations of the previous two steps have moved all the bundling and unbundling of configurations into this one expression. Further note that match immediately unbundles the command list and stack that are bundled together by step. This inefficiency can be avoided by replacing this expression with one of the form (step cmds stack E return ) where E return does not call config. Figure out what E return should be, and update the interpreter to reflect this change. Once you have succeeded at this step, you will have removed all bundling and unbundling of configurations from final-stack and step. d. As a further improvement, in-line the code for step where it appears in the body of final-stack. If you were given a specification of final-stack and asked to write it from scratch, it is easy to imagine that you might come up with something resembling the final version that results from the above derivation. The point of this exercise is that this version can actually be derived from the interpreter presented in this chapter by a series of semantics-preserving transformations. Exercise 1.9 Write a parser that parses PostFix2 programs into the program and command datatypes. Exercise 1.10 Write an operational style interpreter for the EL language in Scheme+. The procedure embodying the interpreter should take two arguments: an input value and a datatype instance representing an EL program. Exercise 1.11 a. Restructure the operational style PostFix interpreter so that commands can be specified and added in modular way via a define-command procedure. For example, it should be possible to express the integer and pop commands as follows: 15

20 Chapter 1 Definitional Interpreters (define-command int (lambda (n) (lambda (cmds stack) (config cmds (cons (int->op-val n) stack))))) (define-command pop (lambda () (lambda (cmds stack) (match stack ((cons _ s) (config cmds s)) (_ (error "POP: empty stack")))))) You may assume that PostFix programs are represented in an appropriate s- expression notation rather than as datatype instances. b. Use define-command to define the following commands: i. rot (Exercise??) ii. dup (Section??) iii. pair (Exercise??) iv. for (Exercise??) v. repeat (Exercise??) c. What modifications would need to be made to the restructured system in order to handle the I, def, and get commands from Exercise??? Exercise 1.12 Write an operational style PostFix interpreter in your favorite programming language (if your favorite language is already Scheme or Scheme+, choose another). How easy would it be to express the modular restructuring suggested in Exercise 1.11 in your chosen language? A Denotational Style Interpreter for PostFix An operational style interpreter has the form of an iteration over a configuration. 6 In contrast, a denotational style interpreter has the form of a recursion that determines the meaning of a phrase in terms of the meanings of its component parts. 6 Not all forms of operational semantics involve iterating over configurations. Here the phrase operational style really means SOS style. 16

21 1.2.2 A Denotational Style Interpreter for PostFix It is fairly straightforward to translate a denotational semantics description into an interpreter in a base language that supports first-class procedures. Procedures are said to be first-class when they can be created on the fly, named by variables, passed as arguments, returned as results, and stored in data structures. These capabilities, which are present in such languages as Scheme, ML, Haskell, and Dylan, make it easy to model the heavy use of higher-order functions within a denotational description. Denotational specifications are significantly trickier to model in languages that do not support first-class procedures. Here we will demonstrate how to write a denotational style PostFix interpreter in Scheme+. We could simply transliterate the denotational specification in Figures?? and?? into Scheme+, but for variety we will take a different tack that underscores the power of first-class procedures in Scheme+. (We leave the more direct transliteration as an exercise.) We assume the same syntactic datatypes that were used for the operational style interpreter. However, here the class of stack values will be different. Executable sequences will not be represented by syntactic command sequences, but by stack transform procedures that map stacks to stacks: (define-datatype den-val (int->den-val int) (xform->den-val (-> (stack) stack))) It will be helpful to introduce a few stack abstractions. 7 A stack is a list of denotable values that can be extended by a (curried) push procedure: (define (empty-stack) ()) (define (push val) (lambda (stack) (cons val stack))) Our means of disassembling a stack will be the with-value procedure: 7 The stack abstractions are not an essential feature of the denotational style, but simply a matter of convenience. We could well have used such abstractions in the operational style interpreter; or we could modify the denotational style interpreter to use pattern matching on stacks instead of the stack abstractions. 17

22 Chapter 1 Definitional Interpreters (define (with-value proc) (lambda (stack) (match stack ((null) (error "Empty stack")) ((cons v s) ((proc v) s))))) with-value takes a procedure and a stack (in curried fashion) and applies the procedure to the head and tail of a stack (also in curried fashion). The Scheme+ error form is used to signal an error if the stack is empty. For example, here s how with-value can be used to extract the top and rest elements of a stack: (define top (with-value (lambda (top) (lambda (rest) top)))) (define pop (with-value (lambda (top) (lambda (rest) rest)))) with-value can be specialized into two handy procedures that check if the top stack value is of a certain type and complain if it isn t: (define (with-integer proc) (with-value (lambda (v) (match v ((int->den-val i) (proc i)) (_ (error "Transform where integer expected")))))) (define (with-transform proc) (with-value (lambda (v) (match v ((xform->den-val t) (proc t)) (_ (error "Integer where transform expected")))))) These definitions illustrate that the same elision of stack references that occurs in function specifications works in Scheme+ as well. Finally, an identity procedure and a means of function composition (written o) will also come in handy: (define (identity x) x) (define (o f g) (lambda (x) (f (g x)))) 18

23 1.2.2 A Denotational Style Interpreter for PostFix The kernel of a denotational style PostFix interpreter appears in Figure 1.6. It consists of a trio of procedures: eval-program, eval-commands, and eval-command. eval-program applies the stack transform associated with its component sequence to an empty stack and returns the top element of the resulting stack. eval-commands and eval-command map syntactic entities (sequences or commands) to stack transforms. The transform of a sequence is either the identity transform (for an empty sequence) or the composition of the tail transform with the head transform. The transforms of individual commands are determined by eval-command, which is the key evaluation dispatch routine for this interpreter. It is worth studying each clause of eval-command in detail and comparing it to the respective valuation clause of the denotational semantics to check that it implements the expected meaning. Perhaps the two subtlest clauses of eval-command are those for handling executable sequence commands and exec commands. Executable sequences are converted to transforms by eval-commands before they are pushed onto the stack. This means that raw sequences never appear on the stack; only their transforms do. The only command that specifically uses these stacked transforms is exec, whose handler could be rewritten as follows: (with-transform identity) = (with-transform (lambda (xform) xform)) = (with-transform (lambda (xform) (lambda (rest) (xform rest)))) The final form makes it clearer that the handler applies the transform at the top of the stack to the rest of the stack. There are several features of this interpreter that are worth emphasizing: What makes this interpreter denotational is that transforms of program phrases are composed from the transforms of their subphrases. For example, if E 1, E 2,..., E n are Scheme+ expressions that stand for PostFix commands, then the procedure created by (eval-commands (list E 1 E 2... E n )) is functionally indistinguishable from the procedure created by 19

24 Chapter 1 Definitional Interpreters ;; eval-program: (-> (program) den-val) (define (eval-program pgm) (match pgm (($prog seq) (top ((eval-commands seq) (empty-stack)))) )) ;; eval-commands: (-> ((listof command)) (-> (stack) stack)) (define (eval-commands seq) (match seq ((null) identity) ((cons com coms) (o (eval-commands coms) (eval-command com))) )) ;; eval-command: (-> (command) (-> (stack) stack)) (define (eval-command cmd) (match cmd ( ($int i) (push (int->den-val i)) ) ( ($seq s) (push (xform->den-val (eval-commands s))) ) ( ($pop) pop ) ( ($swap) (with-value (lambda (v1) (with-value (lambda (v2) (o (push v2) (push v1)))))) ) ( ($sel) (with-value (lambda (else) (with-value (lambda (then) (with-integer (lambda (test) (if (= test 0) (push else) (push then)))))))) ) ( ($exec) (with-transform identity) ) ( ($arithop op) (with-integer (lambda (i1) (with-integer (lambda (i2) (push (int->den-val (op i2 i1))))))) ) ( ($relop op) (with-integer (lambda (i1) (with-integer (lambda (i2) (push (int->den-val (if (op i2 i1) 1 0))))))) ) )) 20 Figure 1.6: The kernel of the denotational style PostFix interpreter.

25 1.2.2 A Denotational Style Interpreter for PostFix (o (o... (o identity (eval-command E n )). (eval-command E 2 )) (eval-command E 1 )) By the properties of the identity and composition procedures, the latter expression can be rewritten as (o (eval-command E n ). (o (eval-command E 2 ) (eval-command E 1 )))) The operational style interpreter does not naturally exhibit this composability. Comparing the types of eval-commands and (regular and curried versions of) step gives insight into this claim. Assume that (listof command) is abbreviated as commands. eval-commands: (-> (commands) (-> (stack) stack)) step: (-> ((* commands stack)) (* commands stack)) step (curried): (-> (commands) (-> (stack) (* commands stack))) (Here, * is a product type constructor, so that (* commands stack) is another way of writing the type of a configuration.) The functional result of applying eval-commands has a type of the form (-> (t) t), which is amenable to n-fold composition. The functional result of applying the curried step has a type that contains an extra commands that gets in the way of n-fold composition. The only natural composition involving step is that of the uncurried step with itself; but this leads to an iterative processing of configurations, not a composable meaning for commands. The interpreter has a conciseness and mathematical flavor that may be unfamiliar to many programmers. These are distinctive features of the 21

26 Chapter 1 Definitional Interpreters functional programming paradigm, which is characterized by extensive use of higher order procedures and an avoidance of imperative features like assignment. A key advantage of the functional programming paradigm is that it promotes mathematical reasoning about programs. The expression equivalences discussed in the previous point are an example of such reasoning. The mathematical flavor of the interpreter would be significantly harder to achieve in a base language that did not support first-class procedures (see Exercise 1.20). We encourage you to develop a better feel for the functional programming paradigm by interacting with and modifying this interpreter. Despite its functional basis, the interpreter takes advantage of Scheme+ s imperative error system to simplify the presentation of the interpreter. In the PostFix denotational semantics from Chapter??, errors were modeled as distinguished summands of the Stack and Answer domains. We could follow this approach in the denotational style interpreter (see Exercise 1.18), but it would reduce clarity by requiring extra checks for specially handling an error stack. These extra checks would not only encumber the readability of the interpreter, they would also burden the interpreter with inefficiency in the form of time overheads (to perform the check) and space overheads (tags to distinguish error stacks from non-error stacks). By relying on Scheme+ s error handling system, we are able to develop a language specification that is in some respects cleaner than the denotational semantics. There are many extensions and improvements that can be made to the denotational style interpreter. As with the operational style interpreter, the interface to this interpreter can be improved by combining it with a parser and a readeval-print loop; extra features can be added to the PostFix language; and the interpreter itself can be structured in a more modular way. The following exercises provide some starting points for exploration. Exercise 1.13 following features: a. rot (Exercise??) b. dup (Section??) c. pair (Exercise??) d. for (Exercise??) e. repeat (Exercise??) Extend the denotational style PostFix interpreter to include the 22

27 1.2.2 A Denotational Style Interpreter for PostFix f. I, def, and get (Exercise??) Exercise 1.14 Modify the denotational style PostFix interpreter to use pop, top, and explicit pattern matching on stack values in place of with-value, with-integer, and with-transform. Use the denotational semantics for PostFix in Figures?? and?? as a guide. Exercise 1.15 The with-integer&stack function introduced in the previous chapter is the metalanguage analogue of the with-integer procedure defined above. It is easy to imagine a similar function, with-value&stack, that corresponds to the with-value procedure. a. Give a definition for with-value&stack. b. In Scheme+, with-value can be used to define both top and pop. Can your version of with-value&stack be used to define both the top and pop functions from the previous chapter? Explain. Exercise 1.16 Modify the denotational style interpreter so that it represents stacks as a list of op-vals rather than a list of den-vals. That is, command sequences should be pushed onto the stacks instead of transforms. Comment on this approach from both semantic and pragmatic perspectives. Exercise 1.17 The denotational style interpreter presented above makes extensive use of higher-order procedures (procedures that take other procedures as arguments and return them as results). It is possible to remove most instances of higher-order procedures from the interpreter by rewriting it so that eval-command, eval-commands, and push all take a stack as a second argument. For example, here s a template for eval-command that includes a sample handler for the sel command: (define (eval-command com stack) (match com. ( ($sel) (match (top (pop (pop stack))) ((int->den-val i) (push (if (= i 0) (top stack) (top (pop stack))) (pop (pop (pop stack)))))) ). )) 23

28 Chapter 1 Definitional Interpreters a. Reimplement the denotational style interpreter in this style. As in Exercise 1.14, you should use stack procedures pop and top and pattern matching on stack values in place of with-value, with-integer, and with-transform. b. What instances of higher-order procedures remain in your modified interpreter? c. Comment on the efficiency of your modified interpreter with respect to the denotational style interpreter presented in the text. Exercise 1.18 Relying on the built-in error system of Scheme+ simplifies the interpreter but makes a read-eval-print loop more annoying to use. Every time a Post- Fix error is encountered it is necessary to restart the PostFix read-eval-print loop. Modify the interpreter by introducing explicit error values and error stacks, a la the PostFix denotational semantics. Make whatever changes are necessary to the rest of the interpreter to handle these error objects. Exercise 1.19 Write a denotational style interpreter for the EL language. Exercise 1.20 Pick your favorite of the following popular languages: Ada, Basic, C, C++, Fortran, Java, Pascal. Implement a denotational style interpreter for PostFix in your chosen language. Discuss any difficulties you encounter An Embedded Interpreter for PostFix2 So far, our notion of interpreter has been a base language program that finds the meaning of a syntactic phrase in the interpreted language. In this perspective, there is a clear distinction between the evaluation procedures (e.g., step, eval-command) and the syntactic phrases (strings, s-expressions, datatype instances, etc.) that they manipulate. Here we introduce an alternate perspective, in which the evaluation procedures themselves are the syntax of the language. We begin by rewriting the eval-command procedure from the previous section so that each dispatch clause becomes a call to a specialist procedure that handles one type of command (Figure 1.7). Note that the names of the specialist routines have been chosen to match the syntax of PostFix2 defined by the s-expression grammar in Figure??. We have done this to underscore a point. Consider the PostFix2 command (int 3). One way to view this command is as an s- expression that can be parsed into an instance of the command datatype and then handed off to eval-command to determine its meaning. But from an alternate 24

29 1.2.3 An Embedded Interpreter for PostFix2 (define (eval-command com) (match com ( ($int i) (int i)) ( ($seq s) (seq s)) ( ($pop) pop ) ( ($swap) (swap)) ( ($sel) (sel)) ( ($exec) (exec)) ( ($arithop op) (arithop op)) ( ($relop op) (relop op)))) (define (int i) (push (int->den-val i))) (define (seq s) (push (xform->den-val (eval-commands s)))) (define (swap) (with-value (lambda (v1) (with-value (lambda (v2) (o (push v2) (push v1))))))) (define (sel) (with-value (lambda (else) (with-value (lambda (then) (with-integer (lambda (test) (if (= test 0) (push else) (push then))))))))) (define (exec) (with-transform identity)) (define (arithop op) (with-integer (lambda (i1) (with-integer (lambda (i2) (push (int->den-val (op i2 i1)))))))) (define (relop op) (arithop (lambda (i1 i2) (if 25 (op i1 i2) 1 0)))) Figure 1.7: Modified version of eval-command in which the dispatch clauses have been reified as procedures.

30 Chapter 1 Definitional Interpreters (define (prog xform) xform) (define (skip) identity) (define (: xform1 xform2) (o xform2 xform1)) ; Note swap! ;; (-> (stack-transform) (+ int executable)) (define (show xform) ((with-value (lambda (v) (lambda (s) (match v ((int->den-val i) (int->sexp i)) ((xform->den-val s) executable))))) (xform (empty-stack)))) Figure 1.8: Extra definitions needed to complete the PostFix2 embedded interpreter. perspective, (int 3) is just a Scheme+ application of the specialist routine int to the number 3. What is fascinating is that both interpretations lead to exactly the same resulting transform! This suggests that it is unnecessary to go through an intermediate syntactic form in order to find the meaning of a PostFix2 phrase. In fact, assembling and disassembling instances of syntactic datatypes for PostFix2 is completely unnecessary. Every element from the s-expression grammar for PostFix2 can be viewed as a Scheme+ application. The Scheme+ virtual machine directly evaluates each such application into the stack transform that it represents. In order for this approach to work, every phrase tag of Post- Fix2 must be defined as a Scheme+ procedure that returns the transform associated with the PostFix2 phrase beginning with that tag. The specialist routines in Figure 1.7 handle most of the phrase tags; the remainder are handled in Figure 1.8. Collectively, these Scheme+ evaluation procedures form what we will call an embedded interpreter for PostFix2. The term embedded suggests that the syntax of the interpreted language is embedded in the application syntax of the base language. Evaluating a program in an embedded interpreter usually requires a procedure that corresponds to the top-level entry point of a traditional interpreter (such as pf-oper and pf-den). In the case of PostFix2, the show procedure 26

31 1.2.3 An Embedded Interpreter for PostFix2 in Figure 1.8 acts as a top-level entry point for the embedded interpreter. For example, here show is used to evaluate a simple PostFix2 program: (show (prog (: (seq (: (int 5) (arithop -))) (: (int 3) (: (swap) (exec)))))) Scheme+ -2 Embedded interpreters may seem like an interesting curiosity, but are they of any real use? Most definitely! They have several advantages over traditional syntax-manipulating interpreters. First of all, there is no need to define any syntactic representations or to parse programs into or unparse programs from these representations. This simplifies the construction of interpreters, and can actually increase their efficiency by removing the need to parse expressions and dispatch on expression type during the interpretation process. (In effect, the writer of the interpreted program has already done all of the necessary dispatches by hand.) Perhaps more important is that embedded interpreters allow for a seamless integration between the base language and the interpreted language. For example, suppose we want to evaluate several PostFix2 programs of the form: (prog (: (int 1) (: (: (int n) (arithop *)) (: (: (int n 1) (arithop *)) (: (: (int n 2) (arithop *)). (: (: (int 1) (arithop *)) (skip))))))) (Such a program computes the factorial of n.) We can easily write a Scheme+ program that abstracts over this PostFix program pattern, and use it to perform the evaluations: 27

32 Chapter 1 Definitional Interpreters (define (fact-prog n) (prog (: (int 1) (fact-commands n)))) (define (fact-commands n) (if (= n 0) (skip) (: (: (int n) (arithop *)) (fact-commands (- n 1))))) (show (fact-prog 4)) Scheme+ 24 (show (fact-prog 5)) Scheme+ 120 In this example, we have essentially used Scheme+ as a kind of macro language to generate PostFix2 programs. The fact that Scheme+ and PostFix2 are at the same level facilitates using PostFix2 expressions within Scheme+ and Scheme+ expressions within PostFix2. Embedded interpreters do have a few minor drawbacks. For one, it s necessary to guarantee that the phrase tags of the embedded language don t conflict with the phrase tags of the base language. For example, we can t rename sel to if in PostFix2 if we want to run the PostFix2 program directly in Scheme+. Additionally, variable names in an embedded language must be quoted to avoid conflicting with variables in the base language, thus complicating the syntax of the embedded language. Various other constructs must also be given an arcane syntax if the embedding is to work properly. But most of these drawbacks are purely syntactic. If we ignore the syntactic idiosyncrasies, embedded interpreters are an extremely flexible way to experiment with language design. They exemplify the powerful idea that a programming language can be represented simply as a set of procedures that are designed to work together. Exercise 1.21 The syntax of PostFix2 is somewhat unwieldy because the sequential composition operator : takes only two commands. Improve the readability of PostFix2 by extending the language to include an n-ary sequential composition operator ::. For example, with the new operator, it should be possible to rewrite as (: (num 1) (: (num 2) (: (num 3) (: (arithop +) (arithop +))))) 28

33 1.3 Summary (:: (num 1) (num 2) (num 3) (arithop +) (arithop +)) Exercise 1.22 Design and implement an embedded interpreter for EL. 1.3 Summary A definitional interpreter is a program written in a base language (the defining language) that defines the semantics of an interpreted language (the defined language). Definitional interpreters are written primarily as a means of specifying the interpreted language, but they can also serve as a starting point for pragmatic explorations. It is possible to capture the essence of an operational or denotational semantics in an interpreter, but interpreters can be written in many other styles as well. An embedded interpreter is one whose syntax is embedded in the syntax of the base language. Definitional interpreters offer many advantages as a framework for semantics, but they suffer some drawbacks as well. On the plus side: The programmatic notation of interpreters is often more accessible to programmers than the mathematical notation of operational and denotational semantics. Programming language features like mutation, non-local exits, and error handling can greatly simplify the description of the defined language. The executable nature of interpreters encourages experimentation with programming language design. Pragmatic issues can be explored within an interpreter. On the minus side: Understanding the semantics of the base language is a prerequisite to understanding the semantics of the interpreted language. Definitional interpreters often lack much of the mathematical structure necessary for formal reasoning. 29

CS251 Programming Languages Handout # 29 Prof. Lyn Turbak March 7, 2007 Wellesley College

CS251 Programming Languages Handout # 29 Prof. Lyn Turbak March 7, 2007 Wellesley College CS5 Programming Languages Handout # 9 Prof. Lyn Turbak March, 00 Wellesley College Postfix: A Simple Stack Language Several exercises and examples in this course will involve the Postfix mini-language.

More information

Denotational Semantics

Denotational Semantics Chapter 4 Denotational Semantics But this denoted a foregone conclusion Othello, William Shakespeare 4.1 The Denotational Semantics Game We have seen how an operational semantics is a natural tool for

More information

PostFix. PostFix command semanccs (except exec) PostFix Syntax. A PostFix Interpreter in Racket. CS251 Programming Languages Fall 2017, Lyn Turbak

PostFix. PostFix command semanccs (except exec) PostFix Syntax. A PostFix Interpreter in Racket. CS251 Programming Languages Fall 2017, Lyn Turbak PostFix A PostFix Interpreter in Racket CS251 Programming Languages Fall 2017, Lyn Turbak Department of Computer Science Wellesley College PostFix is a stack-based mini-language that will be our first

More information

Functional Programming Languages (FPL)

Functional Programming Languages (FPL) Functional Programming Languages (FPL) 1. Definitions... 2 2. Applications... 2 3. Examples... 3 4. FPL Characteristics:... 3 5. Lambda calculus (LC)... 4 6. Functions in FPLs... 7 7. Modern functional

More information

6.001 Notes: Section 8.1

6.001 Notes: Section 8.1 6.001 Notes: Section 8.1 Slide 8.1.1 In this lecture we are going to introduce a new data type, specifically to deal with symbols. This may sound a bit odd, but if you step back, you may realize that everything

More information

PostFix. PostFix command semanccs (except exec) PostFix Syntax. A PostFix Interpreter in Racket. CS251 Programming Languages Spring 2018, Lyn Turbak

PostFix. PostFix command semanccs (except exec) PostFix Syntax. A PostFix Interpreter in Racket. CS251 Programming Languages Spring 2018, Lyn Turbak PostFix A PostFix Interpreter in Racket CS251 Programming Languages Spring 2018, Lyn Turbak Department of Computer Science Wellesley College PostFix is a stack-based mini-language that will be our first

More information

6.001 Notes: Section 6.1

6.001 Notes: Section 6.1 6.001 Notes: Section 6.1 Slide 6.1.1 When we first starting talking about Scheme expressions, you may recall we said that (almost) every Scheme expression had three components, a syntax (legal ways of

More information

CS251 Programming Languages Spring 2016, Lyn Turbak Department of Computer Science Wellesley College

CS251 Programming Languages Spring 2016, Lyn Turbak Department of Computer Science Wellesley College A PostFix Interpreter in Racket CS251 Programming Languages Spring 2016, Lyn Turbak Department of Computer Science Wellesley College PostFix PostFix is a stack- based mini- language that will be our first

More information

Project 5 - The Meta-Circular Evaluator

Project 5 - The Meta-Circular Evaluator MASSACHVSETTS INSTITVTE OF TECHNOLOGY Department of Electrical Engineering and Computer Science 6.001 Structure and Interpretation of Computer Programs Fall Semester, 2005 Project 5 - The Meta-Circular

More information

Functional abstraction. What is abstraction? Eating apples. Readings: HtDP, sections Language level: Intermediate Student With Lambda

Functional abstraction. What is abstraction? Eating apples. Readings: HtDP, sections Language level: Intermediate Student With Lambda Functional abstraction Readings: HtDP, sections 19-24. Language level: Intermediate Student With Lambda different order used in lecture section 24 material introduced much earlier sections 22, 23 not covered

More information

Functional abstraction

Functional abstraction Functional abstraction Readings: HtDP, sections 19-24. Language level: Intermediate Student With Lambda different order used in lecture section 24 material introduced much earlier sections 22, 23 not covered

More information

CSCC24 Functional Programming Scheme Part 2

CSCC24 Functional Programming Scheme Part 2 CSCC24 Functional Programming Scheme Part 2 Carolyn MacLeod 1 winter 2012 1 Based on slides from Anya Tafliovich, and with many thanks to Gerald Penn and Prabhakar Ragde. 1 The Spirit of Lisp-like Languages

More information

6.001 Notes: Section 15.1

6.001 Notes: Section 15.1 6.001 Notes: Section 15.1 Slide 15.1.1 Our goal over the next few lectures is to build an interpreter, which in a very basic sense is the ultimate in programming, since doing so will allow us to define

More information

Functional Programming. Big Picture. Design of Programming Languages

Functional Programming. Big Picture. Design of Programming Languages Functional Programming Big Picture What we ve learned so far: Imperative Programming Languages Variables, binding, scoping, reference environment, etc What s next: Functional Programming Languages Semantics

More information

Introduction to Scheme

Introduction to Scheme How do you describe them Introduction to Scheme Gul Agha CS 421 Fall 2006 A language is described by specifying its syntax and semantics Syntax: The rules for writing programs. We will use Context Free

More information

6.821 Programming Languages Handout Fall MASSACHVSETTS INSTITVTE OF TECHNOLOGY Department of Electrical Engineering and Compvter Science

6.821 Programming Languages Handout Fall MASSACHVSETTS INSTITVTE OF TECHNOLOGY Department of Electrical Engineering and Compvter Science 6.821 Programming Languages Handout Fall 2002 MASSACHVSETTS INSTITVTE OF TECHNOLOGY Department of Electrical Engineering and Compvter Science Problem Set 6 Problem 1: cellof Subtyping Do exercise 11.6

More information

Programming Languages Third Edition. Chapter 7 Basic Semantics

Programming Languages Third Edition. Chapter 7 Basic Semantics Programming Languages Third Edition Chapter 7 Basic Semantics Objectives Understand attributes, binding, and semantic functions Understand declarations, blocks, and scope Learn how to construct a symbol

More information

CSE413: Programming Languages and Implementation Racket structs Implementing languages with interpreters Implementing closures

CSE413: Programming Languages and Implementation Racket structs Implementing languages with interpreters Implementing closures CSE413: Programming Languages and Implementation Racket structs Implementing languages with interpreters Implementing closures Dan Grossman Fall 2014 Hi! I m not Hal J I love this stuff and have taught

More information

Operational Semantics

Operational Semantics Chapter 3 Operational Semantics And now I see with eye serene The very pulse of the machine. She Was a Phantom of Delight, William Wordsworth 3.1 The Operational Semantics Game Consider executing the following

More information

Functional Programming. Pure Functional Programming

Functional Programming. Pure Functional Programming Functional Programming Pure Functional Programming Computation is largely performed by applying functions to values. The value of an expression depends only on the values of its sub-expressions (if any).

More information

Principles of Programming Languages 2017W, Functional Programming

Principles of Programming Languages 2017W, Functional Programming Principles of Programming Languages 2017W, Functional Programming Assignment 3: Lisp Machine (16 points) Lisp is a language based on the lambda calculus with strict execution semantics and dynamic typing.

More information

CIS 194: Homework 3. Due Wednesday, February 11, Interpreters. Meet SImPL

CIS 194: Homework 3. Due Wednesday, February 11, Interpreters. Meet SImPL CIS 194: Homework 3 Due Wednesday, February 11, 2015 Interpreters An interpreter is a program that takes another program as an input and evaluates it. Many modern languages such as Java 1, Javascript,

More information

(Refer Slide Time: 4:00)

(Refer Slide Time: 4:00) Principles of Programming Languages Dr. S. Arun Kumar Department of Computer Science & Engineering Indian Institute of Technology, Delhi Lecture - 38 Meanings Let us look at abstracts namely functional

More information

Intro. Scheme Basics. scm> 5 5. scm>

Intro. Scheme Basics. scm> 5 5. scm> Intro Let s take some time to talk about LISP. It stands for LISt Processing a way of coding using only lists! It sounds pretty radical, and it is. There are lots of cool things to know about LISP; if

More information

Project 5 - The Meta-Circular Evaluator

Project 5 - The Meta-Circular Evaluator MASSACHVSETTS INSTITVTE OF TECHNOLOGY Department of Electrical Engineering and Computer Science 6.001 Structure and Interpretation of Computer Programs Spring Semester, 2005 Project 5 - The Meta-Circular

More information

Intro to Haskell Notes: Part 5

Intro to Haskell Notes: Part 5 Intro to Haskell Notes: Part 5 Adrian Brasoveanu October 5, 2013 Contents 1 Curried functions and related issues 1 1.1 Curried functions......................................... 1 1.2 Partially applied

More information

7. Introduction to Denotational Semantics. Oscar Nierstrasz

7. Introduction to Denotational Semantics. Oscar Nierstrasz 7. Introduction to Denotational Semantics Oscar Nierstrasz Roadmap > Syntax and Semantics > Semantics of Expressions > Semantics of Assignment > Other Issues References > D. A. Schmidt, Denotational Semantics,

More information

Summer 2017 Discussion 10: July 25, Introduction. 2 Primitives and Define

Summer 2017 Discussion 10: July 25, Introduction. 2 Primitives and Define CS 6A Scheme Summer 207 Discussion 0: July 25, 207 Introduction In the next part of the course, we will be working with the Scheme programming language. In addition to learning how to write Scheme programs,

More information

Handout 9: Imperative Programs and State

Handout 9: Imperative Programs and State 06-02552 Princ. of Progr. Languages (and Extended ) The University of Birmingham Spring Semester 2016-17 School of Computer Science c Uday Reddy2016-17 Handout 9: Imperative Programs and State Imperative

More information

Programming Systems in Artificial Intelligence Functional Programming

Programming Systems in Artificial Intelligence Functional Programming Click to add Text Programming Systems in Artificial Intelligence Functional Programming Siegfried Nijssen 8/03/16 Discover thediscover world at the Leiden world University at Leiden University Overview

More information

3.7 Denotational Semantics

3.7 Denotational Semantics 3.7 Denotational Semantics Denotational semantics, also known as fixed-point semantics, associates to each programming language construct a well-defined and rigorously understood mathematical object. These

More information

CSE 413 Languages & Implementation. Hal Perkins Winter 2019 Structs, Implementing Languages (credits: Dan Grossman, CSE 341)

CSE 413 Languages & Implementation. Hal Perkins Winter 2019 Structs, Implementing Languages (credits: Dan Grossman, CSE 341) CSE 413 Languages & Implementation Hal Perkins Winter 2019 Structs, Implementing Languages (credits: Dan Grossman, CSE 341) 1 Goals Representing programs as data Racket structs as a better way to represent

More information

A Small Interpreted Language

A Small Interpreted Language A Small Interpreted Language What would you need to build a small computing language based on mathematical principles? The language should be simple, Turing equivalent (i.e.: it can compute anything that

More information

Organization of Programming Languages CS3200/5200N. Lecture 11

Organization of Programming Languages CS3200/5200N. Lecture 11 Organization of Programming Languages CS3200/5200N Razvan C. Bunescu School of Electrical Engineering and Computer Science bunescu@ohio.edu Functional vs. Imperative The design of the imperative languages

More information

CS61A Discussion Notes: Week 11: The Metacircular Evaluator By Greg Krimer, with slight modifications by Phoebus Chen (using notes from Todd Segal)

CS61A Discussion Notes: Week 11: The Metacircular Evaluator By Greg Krimer, with slight modifications by Phoebus Chen (using notes from Todd Segal) CS61A Discussion Notes: Week 11: The Metacircular Evaluator By Greg Krimer, with slight modifications by Phoebus Chen (using notes from Todd Segal) What is the Metacircular Evaluator? It is the best part

More information

LECTURE 16. Functional Programming

LECTURE 16. Functional Programming LECTURE 16 Functional Programming WHAT IS FUNCTIONAL PROGRAMMING? Functional programming defines the outputs of a program as a mathematical function of the inputs. Functional programming is a declarative

More information

COP4020 Programming Languages. Functional Programming Prof. Robert van Engelen

COP4020 Programming Languages. Functional Programming Prof. Robert van Engelen COP4020 Programming Languages Functional Programming Prof. Robert van Engelen Overview What is functional programming? Historical origins of functional programming Functional programming today Concepts

More information

4.2 Variations on a Scheme -- Lazy Evaluation

4.2 Variations on a Scheme -- Lazy Evaluation [Go to first, previous, next page; contents; index] 4.2 Variations on a Scheme -- Lazy Evaluation Now that we have an evaluator expressed as a Lisp program, we can experiment with alternative choices in

More information

Lists. Michael P. Fourman. February 2, 2010

Lists. Michael P. Fourman. February 2, 2010 Lists Michael P. Fourman February 2, 2010 1 Introduction The list is a fundamental datatype in most functional languages. ML is no exception; list is a built-in ML type constructor. However, to introduce

More information

Module 8: Local and functional abstraction

Module 8: Local and functional abstraction Module 8: Local and functional abstraction Readings: HtDP, Intermezzo 3 (Section 18); Sections 19-23. We will cover material on functional abstraction in a somewhat different order than the text. We will

More information

A Simple Syntax-Directed Translator

A Simple Syntax-Directed Translator Chapter 2 A Simple Syntax-Directed Translator 1-1 Introduction The analysis phase of a compiler breaks up a source program into constituent pieces and produces an internal representation for it, called

More information

1.3. Conditional expressions To express case distinctions like

1.3. Conditional expressions To express case distinctions like Introduction Much of the theory developed in the underlying course Logic II can be implemented in a proof assistant. In the present setting this is interesting, since we can then machine extract from a

More information

Lecture 3: Recursion; Structural Induction

Lecture 3: Recursion; Structural Induction 15-150 Lecture 3: Recursion; Structural Induction Lecture by Dan Licata January 24, 2012 Today, we are going to talk about one of the most important ideas in functional programming, structural recursion

More information

Basic Structure of Denotational Definitions

Basic Structure of Denotational Definitions asic Structure of Denotational Definitions This chapter presents the format for denotational definitions. We use the abstract syntax and semantic algebra formats to define the appearance and the meaning

More information

CPS122 Lecture: From Python to Java

CPS122 Lecture: From Python to Java Objectives: CPS122 Lecture: From Python to Java last revised January 7, 2013 1. To introduce the notion of a compiled language 2. To introduce the notions of data type and a statically typed language 3.

More information

CS152: Programming Languages. Lecture 11 STLC Extensions and Related Topics. Dan Grossman Spring 2011

CS152: Programming Languages. Lecture 11 STLC Extensions and Related Topics. Dan Grossman Spring 2011 CS152: Programming Languages Lecture 11 STLC Extensions and Related Topics Dan Grossman Spring 2011 Review e ::= λx. e x e e c v ::= λx. e c τ ::= int τ τ Γ ::= Γ, x : τ (λx. e) v e[v/x] e 1 e 1 e 1 e

More information

RSL Reference Manual

RSL Reference Manual RSL Reference Manual Part No.: Date: April 6, 1990 Original Authors: Klaus Havelund, Anne Haxthausen Copyright c 1990 Computer Resources International A/S This document is issued on a restricted basis

More information

Programming Languages Third Edition

Programming Languages Third Edition Programming Languages Third Edition Chapter 12 Formal Semantics Objectives Become familiar with a sample small language for the purpose of semantic specification Understand operational semantics Understand

More information

Semantics of programming languages

Semantics of programming languages Semantics of programming languages Informatics 2A: Lecture 27 John Longley School of Informatics University of Edinburgh jrl@inf.ed.ac.uk 21 November, 2011 1 / 19 1 2 3 4 2 / 19 Semantics for programming

More information

Chapter 6 Control Flow. June 9, 2015

Chapter 6 Control Flow. June 9, 2015 Chapter 6 Control Flow June 9, 2015 Expression evaluation It s common in programming languages to use the idea of an expression, which might be a simple object function invocation over some number of arguments

More information

CPS 506 Comparative Programming Languages. Programming Language Paradigm

CPS 506 Comparative Programming Languages. Programming Language Paradigm CPS 506 Comparative Programming Languages Functional Programming Language Paradigm Topics Introduction Mathematical Functions Fundamentals of Functional Programming Languages The First Functional Programming

More information

Lab 6: P.S. it s a stack Due 11:59 pm Monday, Mar 31, 2008

Lab 6: P.S. it s a stack Due 11:59 pm Monday, Mar 31, 2008 1 Assignment Lab 6: P.S. it s a stack Due 11:59 pm Monday, Mar 31, 2008 An interpreter is a program that executes other programs. For example, the Java Virtual Machine that you run using the java program

More information

5 The Control Structure Diagram (CSD)

5 The Control Structure Diagram (CSD) 5 The Control Structure Diagram (CSD) The Control Structure Diagram (CSD) is an algorithmic level diagram intended to improve the comprehensibility of source code by clearly depicting control constructs,

More information

A LISP Interpreter in ML

A LISP Interpreter in ML UNIVERSITY OF OSLO Department of Informatics A LISP Interpreter in ML Mandatory Assignment 1 INF3110 September 21, 2009 Contents 1 1 Introduction The purpose of this assignment is to write an interpreter,

More information

INF4820: Algorithms for Artificial Intelligence and Natural Language Processing. Common Lisp Fundamentals

INF4820: Algorithms for Artificial Intelligence and Natural Language Processing. Common Lisp Fundamentals INF4820: Algorithms for Artificial Intelligence and Natural Language Processing Common Lisp Fundamentals Stephan Oepen & Murhaf Fares Language Technology Group (LTG) August 30, 2017 Last Week: What is

More information

Introduction. chapter Functions

Introduction. chapter Functions chapter 1 Introduction In this chapter we set the stage for the rest of the book. We start by reviewing the notion of a function, then introduce the concept of functional programming, summarise the main

More information

14.1 Encoding for different models of computation

14.1 Encoding for different models of computation Lecture 14 Decidable languages In the previous lecture we discussed some examples of encoding schemes, through which various objects can be represented by strings over a given alphabet. We will begin this

More information

CSE341: Programming Languages Lecture 17 Implementing Languages Including Closures. Dan Grossman Autumn 2018

CSE341: Programming Languages Lecture 17 Implementing Languages Including Closures. Dan Grossman Autumn 2018 CSE341: Programming Languages Lecture 17 Implementing Languages Including Closures Dan Grossman Autumn 2018 Typical workflow concrete syntax (string) "(fn x => x + x) 4" Parsing Possible errors / warnings

More information

6.001 Notes: Section 4.1

6.001 Notes: Section 4.1 6.001 Notes: Section 4.1 Slide 4.1.1 In this lecture, we are going to take a careful look at the kinds of procedures we can build. We will first go back to look very carefully at the substitution model,

More information

SCHEME 7. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. October 29, 2015

SCHEME 7. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. October 29, 2015 SCHEME 7 COMPUTER SCIENCE 61A October 29, 2015 1 Introduction In the next part of the course, we will be working with the Scheme programming language. In addition to learning how to write Scheme programs,

More information

CPSC 411, 2015W Term 2 Midterm Exam Date: February 25, 2016; Instructor: Ron Garcia

CPSC 411, 2015W Term 2 Midterm Exam Date: February 25, 2016; Instructor: Ron Garcia CPSC 411, 2015W Term 2 Midterm Exam Date: February 25, 2016; Instructor: Ron Garcia This is a closed book exam; no notes; no calculators. Answer in the space provided. There are 8 questions on 14 pages,

More information

Modern Programming Languages. Lecture LISP Programming Language An Introduction

Modern Programming Languages. Lecture LISP Programming Language An Introduction Modern Programming Languages Lecture 18-21 LISP Programming Language An Introduction 72 Functional Programming Paradigm and LISP Functional programming is a style of programming that emphasizes the evaluation

More information

Types and Static Type Checking (Introducing Micro-Haskell)

Types and Static Type Checking (Introducing Micro-Haskell) Types and Static (Introducing Micro-Haskell) Informatics 2A: Lecture 13 Alex Simpson School of Informatics University of Edinburgh als@inf.ed.ac.uk 16 October, 2012 1 / 21 1 Types 2 3 4 2 / 21 Thus far

More information

Chapter 11 :: Functional Languages

Chapter 11 :: Functional Languages Chapter 11 :: Functional Languages Programming Language Pragmatics Michael L. Scott Copyright 2016 Elsevier 1 Chapter11_Functional_Languages_4e - Tue November 21, 2017 Historical Origins The imperative

More information

6.821 Programming Languages Handout Fall MASSACHVSETTS INSTITVTE OF TECHNOLOGY Department of Electrical Engineering and Compvter Science

6.821 Programming Languages Handout Fall MASSACHVSETTS INSTITVTE OF TECHNOLOGY Department of Electrical Engineering and Compvter Science 6.821 Programming Languages Handout Fall 2002 MASSACHVSETTS INSTITVTE OF TECHNOLOGY Department of Electrical Engineering and Compvter Science 2002 Midterm There are four problems on this examination. They

More information

Tail Calls. CMSC 330: Organization of Programming Languages. Tail Recursion. Tail Recursion (cont d) Names and Binding. Tail Recursion (cont d)

Tail Calls. CMSC 330: Organization of Programming Languages. Tail Recursion. Tail Recursion (cont d) Names and Binding. Tail Recursion (cont d) CMSC 330: Organization of Programming Languages Tail Calls A tail call is a function call that is the last thing a function does before it returns let add x y = x + y let f z = add z z (* tail call *)

More information

1 Lexical Considerations

1 Lexical Considerations Massachusetts Institute of Technology Department of Electrical Engineering and Computer Science 6.035, Spring 2013 Handout Decaf Language Thursday, Feb 7 The project for the course is to write a compiler

More information

Intro to semantics; Small-step semantics Lecture 1 Tuesday, January 29, 2013

Intro to semantics; Small-step semantics Lecture 1 Tuesday, January 29, 2013 Harvard School of Engineering and Applied Sciences CS 152: Programming Languages Lecture 1 Tuesday, January 29, 2013 1 Intro to semantics What is the meaning of a program? When we write a program, we use

More information

SCHEME AND CALCULATOR 5b

SCHEME AND CALCULATOR 5b SCHEME AND CALCULATOR 5b COMPUTER SCIENCE 6A July 25, 203 In the next part of the course, we will be working with the Scheme programming language. In addition to learning how to write Scheme programs,

More information

Chapter 15. Functional Programming Languages

Chapter 15. Functional Programming Languages Chapter 15 Functional Programming Languages Copyright 2009 Addison-Wesley. All rights reserved. 1-2 Chapter 15 Topics Introduction Mathematical Functions Fundamentals of Functional Programming Languages

More information

6.184 Lecture 4. Interpretation. Tweaked by Ben Vandiver Compiled by Mike Phillips Original material by Eric Grimson

6.184 Lecture 4. Interpretation. Tweaked by Ben Vandiver Compiled by Mike Phillips Original material by Eric Grimson 6.184 Lecture 4 Interpretation Tweaked by Ben Vandiver Compiled by Mike Phillips Original material by Eric Grimson 1 Interpretation Parts of an interpreter Arithmetic calculator

More information

CPS122 Lecture: From Python to Java last revised January 4, Objectives:

CPS122 Lecture: From Python to Java last revised January 4, Objectives: Objectives: CPS122 Lecture: From Python to Java last revised January 4, 2017 1. To introduce the notion of a compiled language 2. To introduce the notions of data type and a statically typed language 3.

More information

CS 360 Programming Languages Interpreters

CS 360 Programming Languages Interpreters CS 360 Programming Languages Interpreters Implementing PLs Most of the course is learning fundamental concepts for using and understanding PLs. Syntax vs. semantics vs. idioms. Powerful constructs like

More information

Parsing Combinators: Introduction & Tutorial

Parsing Combinators: Introduction & Tutorial Parsing Combinators: Introduction & Tutorial Mayer Goldberg October 21, 2017 Contents 1 Synopsis 1 2 Backus-Naur Form (BNF) 2 3 Parsing Combinators 3 4 Simple constructors 4 5 The parser stack 6 6 Recursive

More information

An Explicit Continuation Evaluator for Scheme

An Explicit Continuation Evaluator for Scheme Massachusetts Institute of Technology Course Notes 2 6.844, Spring 05: Computability Theory of and with Scheme February 17 Prof. Albert R. Meyer revised March 3, 2005, 1265 minutes An Explicit Continuation

More information

What s different about Factor?

What s different about Factor? Harshal Lehri What s different about Factor? Factor is a concatenative programming language - A program can be viewed as a series of functions applied on data Factor is a stack oriented program - Data

More information

Types and Static Type Checking (Introducing Micro-Haskell)

Types and Static Type Checking (Introducing Micro-Haskell) Types and Static (Introducing Micro-Haskell) Informatics 2A: Lecture 14 John Longley School of Informatics University of Edinburgh jrl@inf.ed.ac.uk 17 October 2017 1 / 21 1 Types 2 3 4 2 / 21 So far in

More information

Programming Language Pragmatics

Programming Language Pragmatics Chapter 10 :: Functional Languages Programming Language Pragmatics Michael L. Scott Historical Origins The imperative and functional models grew out of work undertaken Alan Turing, Alonzo Church, Stephen

More information

Monads. Mark Hills 6 August Department of Computer Science University of Illinois at Urbana-Champaign

Monads. Mark Hills 6 August Department of Computer Science University of Illinois at Urbana-Champaign Monads Mark Hills mhills@cs.uiuc.edu Department of Computer Science University of Illinois at Urbana-Champaign 6 August 2009 Hills Monads 1 / 19 Overview Overview Hills Monads 2 / 19 Why Monads? Overview

More information

SCHEME 8. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. March 23, 2017

SCHEME 8. 1 Introduction. 2 Primitives COMPUTER SCIENCE 61A. March 23, 2017 SCHEME 8 COMPUTER SCIENCE 61A March 2, 2017 1 Introduction In the next part of the course, we will be working with the Scheme programming language. In addition to learning how to write Scheme programs,

More information

Denotational Semantics. Domain Theory

Denotational Semantics. Domain Theory Denotational Semantics and Domain Theory 1 / 51 Outline Denotational Semantics Basic Domain Theory Introduction and history Primitive and lifted domains Sum and product domains Function domains Meaning

More information

2.2 Syntax Definition

2.2 Syntax Definition 42 CHAPTER 2. A SIMPLE SYNTAX-DIRECTED TRANSLATOR sequence of "three-address" instructions; a more complete example appears in Fig. 2.2. This form of intermediate code takes its name from instructions

More information

Fundamentals of Artificial Intelligence COMP221: Functional Programming in Scheme (and LISP)

Fundamentals of Artificial Intelligence COMP221: Functional Programming in Scheme (and LISP) Fundamentals of Artificial Intelligence COMP221: Functional Programming in Scheme (and LISP) Prof. Dekai Wu Department of Computer Science and Engineering The Hong Kong University of Science and Technology

More information

Design Concepts in Programming Languages

Design Concepts in Programming Languages Design Concepts in Programming Languages Chapter 1: Introduction Franklyn Turbak and David Gifford with Mark A. Sheldon The MIT Press Cambridge, Massachusetts London, England 1 Introduction Order and

More information

CS251 Programming Languages Handout # 47 Prof. Lyn Turbak May 22, 2005 Wellesley College. Scheme

CS251 Programming Languages Handout # 47 Prof. Lyn Turbak May 22, 2005 Wellesley College. Scheme CS251 Programming Languages Handout # 47 Prof. Lyn Turbak May 22, 2005 Wellesley College 1 Scheme Overview Scheme Scheme is a block-structured, lexically-scoped, properly tail-recursive dialect of Lisp

More information

CSSE 304 Assignment #13 (interpreter milestone #1) Updated for Fall, 2018

CSSE 304 Assignment #13 (interpreter milestone #1) Updated for Fall, 2018 CSSE 304 Assignment #13 (interpreter milestone #1) Updated for Fall, 2018 Deliverables: Your code (submit to PLC server). A13 participation survey (on Moodle, by the day after the A13 due date). This is

More information

Lecture #24: Programming Languages and Programs

Lecture #24: Programming Languages and Programs Lecture #24: Programming Languages and Programs A programming language is a notation for describing computations or processes. These range from low-level notations, such as machine language or simple hardware

More information

Assignment 5. Introduction

Assignment 5. Introduction Assignment 5 Introduction The objectives of this assignment are to exercise a few advanced object oriented programming and basic data structures concepts. The first mini-goal is to understand that objects

More information

C311 Lab #3 Representation Independence: Representation Independent Interpreters

C311 Lab #3 Representation Independence: Representation Independent Interpreters C311 Lab #3 Representation Independence: Representation Independent Interpreters Will Byrd webyrd@indiana.edu February 5, 2005 (based on Professor Friedman s lecture on January 29, 2004) 1 Introduction

More information

4/19/2018. Chapter 11 :: Functional Languages

4/19/2018. Chapter 11 :: Functional Languages Chapter 11 :: Functional Languages Programming Language Pragmatics Michael L. Scott Historical Origins The imperative and functional models grew out of work undertaken by Alan Turing, Alonzo Church, Stephen

More information

Lecture Notes on Contracts

Lecture Notes on Contracts Lecture Notes on Contracts 15-122: Principles of Imperative Computation Frank Pfenning Lecture 2 August 30, 2012 1 Introduction For an overview the course goals and the mechanics and schedule of the course,

More information

Week - 01 Lecture - 04 Downloading and installing Python

Week - 01 Lecture - 04 Downloading and installing Python Programming, Data Structures and Algorithms in Python Prof. Madhavan Mukund Department of Computer Science and Engineering Indian Institute of Technology, Madras Week - 01 Lecture - 04 Downloading and

More information

Handout 10: Imperative programs and the Lambda Calculus

Handout 10: Imperative programs and the Lambda Calculus 06-02552 Princ of Progr Languages (and Extended ) The University of Birmingham Spring Semester 2016-17 School of Computer Science c Uday Reddy2016-17 Handout 10: Imperative programs and the Lambda Calculus

More information

15-122: Principles of Imperative Computation, Fall 2015

15-122: Principles of Imperative Computation, Fall 2015 15-122 Programming 5 Page 1 of 10 15-122: Principles of Imperative Computation, Fall 2015 Homework 5 Programming: Clac Due: Thursday, October 15, 2015 by 22:00 In this assignment, you will implement a

More information

LECTURE 17. Expressions and Assignment

LECTURE 17. Expressions and Assignment LECTURE 17 Expressions and Assignment EXPRESSION SYNTAX An expression consists of An atomic object, e.g. number or variable. An operator (or function) applied to a collection of operands (or arguments)

More information

Data Types The ML Type System

Data Types The ML Type System 7 Data Types 7.2.4 The ML Type System The following is an ML version of the tail-recursive Fibonacci function introduced Fibonacci function in ML in Section 6.6.1: EXAMPLE 7.96 1. fun fib (n) = 2. let

More information

CS112 Lecture: Primitive Types, Operators, Strings

CS112 Lecture: Primitive Types, Operators, Strings CS112 Lecture: Primitive Types, Operators, Strings Last revised 1/24/06 Objectives: 1. To explain the fundamental distinction between primitive types and reference types, and to introduce the Java primitive

More information

Introduction to Typed Racket. The plan: Racket Crash Course Typed Racket and PL Racket Differences with the text Some PL Racket Examples

Introduction to Typed Racket. The plan: Racket Crash Course Typed Racket and PL Racket Differences with the text Some PL Racket Examples Introduction to Typed Racket The plan: Racket Crash Course Typed Racket and PL Racket Differences with the text Some PL Racket Examples Getting started Find a machine with DrRacket installed (e.g. the

More information

Principles of Programming Languages COMP251: Functional Programming in Scheme (and LISP)

Principles of Programming Languages COMP251: Functional Programming in Scheme (and LISP) Principles of Programming Languages COMP251: Functional Programming in Scheme (and LISP) Prof. Dekai Wu Department of Computer Science and Engineering The Hong Kong University of Science and Technology

More information

Compilers. Prerequisites

Compilers. Prerequisites Compilers Prerequisites Data structures & algorithms Linked lists, dictionaries, trees, hash tables Formal languages & automata Regular expressions, finite automata, context-free grammars Machine organization

More information