Design Concepts in Programming Languages Scheme Supplement
|
|
- Marybeth Reeves
- 5 years ago
- Views:
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
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 informationDenotational 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 informationPostFix. 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 informationFunctional 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 information6.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 informationPostFix. 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 information6.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 informationCS251 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 informationProject 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 informationFunctional 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 informationFunctional 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 informationCSCC24 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 information6.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 informationFunctional 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 informationIntroduction 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 information6.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 informationProgramming 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 informationCSE413: 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 informationOperational 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 informationFunctional 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 informationPrinciples 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 informationCIS 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)
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 informationIntro. 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 informationProject 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 informationIntro 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 information7. 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 informationSummer 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 informationHandout 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 informationProgramming 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 information3.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 informationCSE 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 informationA 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 informationOrganization 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 informationCS61A 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 informationLECTURE 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 informationCOP4020 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 information4.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 informationLists. 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 informationModule 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 informationA 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 information1.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 informationLecture 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 informationBasic 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 informationCPS122 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 informationCS152: 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 informationRSL 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 informationProgramming 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 informationSemantics 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 informationChapter 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 informationCPS 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 informationLab 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 information5 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 informationA 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 informationINF4820: 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 informationIntroduction. 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 information14.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 informationCSE341: 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 information6.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 informationSCHEME 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 informationCPSC 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 informationModern 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 informationTypes 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 informationChapter 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 information6.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 informationTail 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 information1 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 informationIntro 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 informationSCHEME 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 informationChapter 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 information6.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 informationCPS122 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 informationCS 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 informationParsing 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 informationAn 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 informationWhat 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 informationTypes 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 informationProgramming 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 informationMonads. 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 informationSCHEME 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 informationDenotational 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 information2.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 informationFundamentals 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 informationDesign 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 informationCS251 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 informationCSSE 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 informationLecture #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 informationAssignment 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 informationC311 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 information4/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 informationLecture 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 informationWeek - 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 informationHandout 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 information15-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 informationLECTURE 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 informationData 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 informationCS112 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 informationIntroduction 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 informationPrinciples 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 informationCompilers. 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