Presentation is loading. Please wait.

Presentation is loading. Please wait.

The Role of the Study of Programming Languages in the Education of a Programmer Dan Friedman Indiana University.

Similar presentations


Presentation on theme: "The Role of the Study of Programming Languages in the Education of a Programmer Dan Friedman Indiana University."— Presentation transcript:

1 The Role of the Study of Programming Languages in the Education of a Programmer Dan Friedman Indiana University

2 Why study programming languages? First: To teach one to avoid bad ideas –A little history about dynamic scope –A small discussion about types –Improper implementation of tail- calls 2

3 Second: To embrace good ideas in a representationally-independent fashion –Resolving tail-calls until something better comes along! 3 Why study programming languages?

4 Other roles: Three qoutes from students: –Jon Rossie: The life of a programming language expert in a world that does not know what one is and does not understand what one does... 4 Why study programming languages?

5 Every program is an interpreter! Programs are data All data are programs The hardware is an interpreter A compiler is an interpreter A type checker is an interpreter! 5

6 Data must be interpreted What is the interpretation of “5” What is the interpretation of “V” –A character? –A number? What is the interpretation of a function that takes “XIV” and produces XV”? –The successor function? –The predecessor function? 6

7 Essentials of Programming Languages Programmers like several PL Programmers are upset if things are difficult to do in their favorite PL Studying PL alleviates this problem Programmers are expected to learn PL But there are certain language design issues that are essential... 7

8 Other roles: Three quotes from students: –Anurag Mendhekar: I believe in the power of abstraction in software development... 8 Why study programming languages?

9 Separate what you want to do from how are you going to do it! Details are for later, after the ideas are developed and prototyped Details can be the implementation of the language design or of the language itself 9 Why study programming languages?

10 What do we mean by the study? The application of one’s mental faculties to the acquisition of knowledge in a particular field or to a specific subject One acquires such knowledge by developing a firm foundation of concepts to absorb knowledge Modeling the artifact one is studying 10

11 What do we mean by the study? For instance: knowing that a language passes its parameters by value is better than a description of what call-by-value is If one knows call-by-value -calculus one only needs to know what items in the language are values! 11

12 What do we mean by the study of programming languages? The semi-formal analysis of programming language concepts and their underlying principles that have lasted beyond a decade of their discovery! Syntax should not be the focus! 12

13 A programming languages concept: Lexical Scope Free variables are bound to values when procedures are created Around since Algol 60 Logicians have used it for much longer, as quantifiers  and  rely on lexical scope 13

14 Lexical Scope > let a = 3 in let p = proc (x) +(x, a) a = 5 in *(a, p(2))  *(5, +(2, 3)) = 25 14

15 ( x E Tuple*)== (ormap( (x)E)Tuple*) ( x E Tuple*)== (andmap( (x)E)Tuple*) and 15   The power of -abstraction Relational database system operators (relying on lexical scope)

16 To teach one to avoid bad ideas –A little history about dynamic scope –A small discussion about types –Improper implementation of tail- calls 16 Why study programming languages?

17 Dynamic Scope > let a = 3 in let p = proc (x) +(x, a) a = 5 in *(a, p(2))  *(5, +(2, 5)) = 35 17

18 When you study scoping... You are likely to discover dynamic scope first You need to know why dynamic scope is a mistake! You need to know about the choice of a name for a bound variable 18

19  -substitution ( (x) x) = ( (y) y) To change the x to y you need to use a name that is not used in the expression 19 = ( (y) ( (x)(y x))) ( (z) ( (x)(z x)))

20 (define map ( (f ls) (if (null? ls) ’ () (cons (f (first ls)) (map f (rest ls)))))) Consider the definition of map 20

21 What happens when  -rule works with map, with lexical scope? (let ((ls ’ (1 2 3 4))) (map ( (x) (cons x ls)) ls)) > ((1 1 2 3 4) (2 1 2 3 4) (3 1 2 3 4) (4 1 2 3 4)) 21

22 What does this return if let and are dynamically scoped? It starts out the same: ( (1 1 2 3 4)... But, on the second recursion, ls gets smaller, so it affects the ls inside the definition of map ! Isn’t that a surprise? 22

23 Should any language designer be allowed to inflict such horrifying thoughts on a language user? >((1 1 2 3 4) (2 2 3 4) (3 3 4) (4 4)) 23

24 Equations for Lexical and Dynamic Binding =( (arg)( (env)E[M]env[x arg])) versus =( (arg)( (enw)E[M]env[x arg])) 24 E[( (x)M)]env  

25 To teach one to avoid bad ideas –A little history about dynamic scope –A small discussion about types –A disaster in the implementation of tail-calls 25 Why study programming languages?

26 What is wrong with this Scheme program? Nothing? 26 (if (= n 0) (+ n 5) (not (= (length ls) 4)))

27 Nothing? 27 How about this one? ((if (= n 0) 5 ( (x) (+ x 7))) 6)

28 What is wrong with this Scheme program? The value of the conditional is either a number or a boolean! 28 (if (= n 0) (+ n 5) (not (= (length ls) 4)))

29 The value of the conditional is either a number or a function! So, sometimes we will apply a function to a number, but other times we will apply the number five to a number! 29 How about this one? ((if (= n 0) 5 ( (x) (+ x 7))) 6)

30 To teach one to avoid bad ideas –A little history about dynamic scope –A small discussion about types –Improper implementation of tail- calls 30 Why study programming languages?

31 The problem Most implementations of Java and C do not handle tail-calls properly; the problem is not just recursive calls, but also method calls! Java does not encourage the writing of recursive programs But Java can be ok... assume that Scheme (Lisp) code can be easily converted to Java-code 31

32 The solution Use correctness-preserving transformations Transformations are simple and logical, Should be used when the opportunity arises Solve the problem of language implementation... for Java and C! 32

33 (define E ( (e r) (cases expression e (lit-exp (datum) datum) (var-exp (id) (r id)) (primapp-exp (prim rands) (prim (map ( (x) (E x r)) rands))) (if-exp (test-e true-e false-e) (if (E test-e r) (E true-e r) (E false-e r))) (proc-exp (ids body) ( (args) (E body ( (id) (if (memq id ids) (lookup id ids args) (r id)))))) (app-exp (rator rands) ((E rator r) (map ( (x) (E x r)) rands)))))) 33

34 DO NOT PANIC WHILE READING THE CODE THAT FOLLOWS DETAILS ARE FOR LATER 34

35 (define sum ( (n) (if (= n 0) 0 (+ n (sum (- n 1)))))) The call: > (sum 1000000) sum program with a non-tail call 35 Nontail calls: embedded calls

36 (define sum ( (n acc ) (if (= n 0) acc (sum (- n 1)(+ n acc ))))) The call: > (sum 1000000 0) 36 Implementation using an accumulator so that it would contain no non-tail calls. Programs in Tail form

37 The problem Running these programs in Java would not give the right answer: almost guaranteed! We are relying on a control stack, which is not very deep: Java assumes that most of your programs have a lot of while loops and assignment statements! The two Java programs for sum will produce the same wrong result: an exception! 37

38 Second: To embrace good ideas in a representationally-independent fashion –Resolving tail-calls until something better comes along! 38 Why study programming languages?

39 (define n) (define acc ) (define sum ( () (if (= n 0) acc {( acc (+ n acc)) ;; (begin(set! ( n (- n 1)) (sum)})))   39 Transforming the tail form into register form Substitute the call for assignment statements:

40 Transforming a Java program to make it work! > {( acc 0);assigment ( n 1000000) (sum)} ;goto 40   The execution: 1. Set the registers 2. Make the call! But, In Java, this program still blows up!

41 Next: we transform the program into suspended-goto form ( define sum ( () (if (= n 0) acc {( acc (+ n acc )) ( n (- n 1)) ( ()(sum))} > {( acc 0) ( n 1000000) ( ()(sum))} 41    

42 But( ()( f )) = f, in -calculus -rule restricted to variables: the -suspended-goto form. 42 (define sum ( () (if (= n 0) acc {( acc (+ n acc )) ( n (- n 1)) sum}))) > {( acc 0) ( n 1000000) sum}      

43 Executing a program in suspended form In Java, we are required to use a while loop, which is primitive in Java and does not grow the stack We need to define a variable false instead of acc to terminate the while loop The accumulator is de-referenced at the end of the computation 43

44 44 (define sum ( () (if (= n 0) false {( acc (+ n acc )) ( n (- n 1)) sum})))   The new program

45 45 The control loop: (define run ( () {(while (sum) ’ no-op) acc })) The call: > {( acc 0) ( n 1000000) (run)}   The new program

46 Independence of parameters We introduce a new register action to avoid reliance on the “return” facilities The value returned from sum is placed in action No reliance on arguments being passed nor the value being returned, leading to trampoline form 46

47 47 (define action ) (define sum ( () (if (= n 0) ( action false) {( acc (+ n acc )) ( n (- n 1)) ( action sum)})))     Independence of parameters

48 48 The control loop: (define run ( () {(while action ( action )) acc })) The call: > {( acc 0) ( n 1000000) ( action sum) (run)}    Independence of parameters

49 Can every program be written in tail form? The two initial programs for sum (tail and non-tail forms) return the same result but compute differently; The first numbers added in the non-tail form were 1 and 0, but in the tail form, the first two numbers added were n and 0 (we relied on associative and commutative properties of sum) We cannot always do that! 49

50 Putting programs into tail form An interpreter can be put into tail-form (Essentials, Chap. 7) An interpreter can also be put into register form! But we need to transform any program to one that can be written and run in any host language 50

51 Putting programs into tail form There is an algorithm in: “Call-by-value, call-by-name and the lambda calculus” by Gordon Plotkin, 1975. Fate lent a hand: Amr Sabry and Matthias Felleisen discovered a new algorithm (Chapter 8, of the second edition of “Essentials...”) 51

52 Putting programs into tail form First, let’s transform the sum program into preregister-tail form without using the associative and commutative properties Then, transform the program into trampoline form This kind of code is said to be in continuation-passing style 52

53 The original sum program... again 53 ( (n) (if (= n 0) 0 (+ n (sum (- n 1)))))) The call: > (sum 1000000) The program: (define sum

54 Continuations Programs passed as arguments to other programs indicating what to do next We need to add such an argument to every program that will be put into continuation- passing style This has to be done for every -expression of the original program ! 54

55 (define id ( ( acc ) acc )) (define sum ( (n cont ) ( cont (if (= n 0) 0 (+ n (sum (- n 1) id )))))) > (sum 1000000 id ) The sum in continuation-passing style 55

56 ( define sum ( (n cont ) (if (= n 0) ( cont 0) ( cont (+ n (sum (- n 1) id )))))) 56 The sum in continuation-passing style Let’s push the continuation through the branches of the if-expression: > (sum 1000000 id ) The call:

57 (define sum ( (n cont ) (if (= n 0) ( cont 0) (sum (- n 1) ( ( acc ) ( cont (+ n acc ))))))) 57 The sum in continuation-passing style Let’s push the continuation through the embedded sum:

58 Continuations The program has been transformed into tail- form Next: we must dereference every free variable used in the continuation 58

59 The continuation with the free variables: ( ( acc ) ( cont (+ n acc ))) The two free variables are n and cont, so we replace the entire expression by (let ((n n)( cont cont )) ( ( acc ) ( cont (+ n acc )))) 59

60 (define sum ( (n cont ) (if (= n 0) ( cont 0) (sum (- n 1) (let ((n n)( cont cont )) ( ( acc ) ( cont (+ n acc )))))))) 60 The preregistered tail-form:

61 ( ( acc ) acc ) or ( ( acc ) ( cont (+ n acc ))) 61 But we might be in a language that does not directly support higher-order functions such as: We can still use continuation-passing style, but we need to change the representation of continuations

62 Solution: Replace calls of the form (cont s) with ( apply-cont cont s): (define sum ( (n cont ) (if (= n 0) (apply-cont cont 0) (sum (- n 1) (let ((n n) ( cont cont )) ( ( acc ) (apply-cont cont (+ n acc ))) ))))) 62

63 Solution: Replace calls of the form (cont s) with (apply-cont cont s). (define apply-cont ( ( cont acc ) ( cont acc ))) 63 Where: With this, we come to the representation- independent-preregister-tail form, as follows:

64 (define id ’ ()) (define sum ( (n cont ) (if (= n 0) (apply-cont cont 0) (sum (- n 1) (cons n cont ))))) (define apply-cont ( ( cont acc ) (if (null? cont ) acc (apply-cont(cdr cont ) (+ (car cont ) acc ))))) > (sum 1000000 id ) 64

65 Where are we? All calls are tail-calls The dereferencing of free variables happens automatically when cons is invoked So, we can proceed as we did in the tail form definitions of sum leading to the trampoline form, as follows: 65

66 (define sum ( () (if (= n 0) {( cont cont ) ( acc 0) ( action apply-cont)} {( cont (cons n cont )) ( n (- n 1)) ( action sum)})))       Trampoline form 66

67 (define apply-cont ( () (if (null? cont ) ( action false) {( acc (+ (car cont ) acc )) ( cont (cdr cont )) ( action apply-cont)}))) > {( cont id ) ( n 1000000) ( action sum) (run)} 67       

68 Modeling the apply-cont dispatch Using the abstract method apply-cont by inheriting from the associated abstract class of continuation types (A Little Java, A Few Patterns, Chap. 1) There will be two subclasses –For continuation modeled by the empty list –For continuations modeled by cons 68

69 Continuations as procedures Alternatively, we can go back to the version of continuations represented as procedures We can treat those procedures as actions, as follows: 69

70 (define id ( () ( action false))) (define sum ( () (if (= n 0) {( acc 0) ( action cont ) } {( cont (let ((n n)( cont cont )) ( () {( acc (+ n acc )) ( action cont )}))) ( n (- n 1)) ( action sum)})))         70

71 The moral... A problem with poor implementation technology can be solved with good correctness-preserving transformations Our approach relies heavily on such transformations You will learn to develop your own perspective on how to implement things elegantly! 71

72 Other roles: Three quotes from students: –Jonathan Sobel: “That was the amazing part: I had produced a program that I could not have written, and in any case would not have wanted to write.” 72 Why study programming languages?

73 Conclusion The study of programming languages yields general-purpose tools that allow you to do things that are too hard to do without them. Learning these tools is the standard fare for researchers in programming languages 73

74 Conclusion A final quote from Christopher Strachey: –I always worked with programming languages because it seemed to me that until you could understand those, you really couldn’t understand computers. Understanding them doesn’t really mean only being able to use them. A lot of people can use them without understanding them. 74

75 For those who wish to run this code in Scheme instead of Java or C, here is the definition of while. (define-syntax while (syntax-rules () ((while exp stmts...) (let loop () (if exp (begin stmts...(loop))) )))) 75


Download ppt "The Role of the Study of Programming Languages in the Education of a Programmer Dan Friedman Indiana University."

Similar presentations


Ads by Google