Chapter 1 - Introduction 4/23/2017 Chapter 1 - Introduction Programming Languages: Principles and Practice, 2nd Ed. Kenneth C. Louden © Kenneth C. Louden, 2003
What is a Programming Language? Definition: A programming language is a notational system for describing computation in machine-readable and human-readable form. Chapter 1 K. Louden, Programming Languages
K. Louden, Programming Languages Computation: Described by a Turing Machine - a very simple computer that can carry out all known computations (albeit not very efficiently). A programming language is Turing complete if it can be used to describe any computation performed by a Turing Machine. Chapter 1 K. Louden, Programming Languages
What is needed for Turing completeness? Virtually nothing: A programming language is Turing complete provided it has integer variables and arithmetic and sequentially executes statements, which include assignment, selection (if) and loop (while) statements. Even if statements are unnecessary (see exercises). Chapter 1 K. Louden, Programming Languages
Machine-readability: Also not a huge requirement: Basically, the existence of a (more or less) linear-time translation algorithm. Usually boils down to: The syntax must be given by a context-free grammar. (See Chapter 4.) Chapter 1 K. Louden, Programming Languages
K. Louden, Programming Languages Human-readability: This is the real issue! Virtually all the complex details of a programming language are there to (supposedly) enhance human readability. Still not very well understood. Is strongly dependent on good choice of abstractions. Chapter 1 K. Louden, Programming Languages
What about human “writability??” Aren’t programming languages there to promote the writing of programs, not the reading of them? Nonsense! Writability is a hacker’s goal: Perl is very writable, but try to read it! Readability is the real goal: many people are going to have to read your program after you have written it. Chapter 1 K. Louden, Programming Languages
K. Louden, Programming Languages Abstractions: Chapter 1 K. Louden, Programming Languages
K. Louden, Programming Languages Language Paradigms: Imperative (procedural): traditional sequential programming (passive data, active control). Characterized by variables, assignment, and loops. Object-oriented: data-centric, data controls its own use, action by request to data objects. Characterized by messages, instance variables, and protection. Functional: passive data, but no sequential control; all action by function evaluation (“call”), particularly recursion. No variables! Chapter 1 K. Louden, Programming Languages
Language Paradigms (cont.): Logic: Assertions are the basic data; logic inference the basic control. Again, no sequential operation. Parallel: well, maybe not really a paradigm, but some think so. Again, no sequential operation. “Declarative”: Logic and functional paradigms share this property: state “what” needs computing, not “how” (sequence). Chapter 1 K. Louden, Programming Languages
Languages and paradigms Imperative: C, Pascal, core Ada, FORTRAN Functional: Lisp (Scheme), ML, Haskell Object-oriented: C++, Java, Smalltalk Logic: Prolog Parallel: Java (threads), Ada (tasks) Chapter 1 K. Louden, Programming Languages
Are functional languages Turing-complete? Previous theorem on Turing-completeness (slide 4) depends on the existence of variables and loops. Functional programs do not have variables or loops. Can all computation be expressed? Yes!: A programming language is Turing complete if it has integer values, arithmetic functions on those values, and if it has a mechanism for defining new functions using existing functions, selection, and recursion. Chapter 1 K. Louden, Programming Languages
K. Louden, Programming Languages Examples in three languages (Euclid’s gcd algorithm): Compute the greatest common divisor of two integers input by the user, and print the result. For example, the gcd of 15 and 10 is 5. Chapter 1 K. Louden, Programming Languages
K. Louden, Programming Languages C (Figure 1.7, p. 26): #include <stdio.h> int gcd(int u, int v) /* “functional” version */ { if (v == 0) return u; else return gcd (v, u % v); /* “tail” recursion */ } main() /* I/O driver */ { int x, y; printf("Input two integers:\n"); scanf("%d%d",&x,&y); printf("The gcd of %d and %d is %d\n", x,y,gcd(x,y)); return 0; Chapter 1 K. Louden, Programming Languages
K. Louden, Programming Languages Java (Figure 1.9, p. 27): import java.io.*; class IntWithGcd { public IntWithGcd( int val ) { value = val; } public int getValue() { return value; } public int gcd ( int v ) { int z = value; /* “imperative” version */ int y = v; while ( y != 0 ) { int t = y; y = z % y; z = t; } return z; private int value; Chapter 1 K. Louden, Programming Languages
K. Louden, Programming Languages Java (continued): class GcdProg /* driver */ { public static void main (String args[]) { System.out.println("Input two integers:"); BufferedReader in = new BufferedReader( new InputStreamReader(System.in)); try /* must handle I/O exceptions */ { IntWithGcd x = /* create an object */ new IntWithGcd(Integer.parseInt(in.readLine())); int y = Integer.parseInt(in.readLine()); System.out.print("The gcd of " + x.getValue() + " and " + y + " is "); System.out.println(x.gcd(y)); } catch ( Exception e) { System.out.println(e); System.exit(1); } } Chapter 1 K. Louden, Programming Languages
K. Louden, Programming Languages Scheme (Figure 1.5, p. 17): (define (gcd u v) (if (= v 0) u (gcd v (modulo u v)))) Chapter 1 K. Louden, Programming Languages
K. Louden, Programming Languages Scheme (Figure 11.7, p. 486): (define (euclid) ; sequential! (display "enter two integers:") (newline) ; goes to next line on screen (let ((u (read)) (v (read))) (display "the gcd of ") (display u) (display " and ") (display v) (display " is ") (display (gcd u v)) (newline))) Chapter 1 K. Louden, Programming Languages
Paradigm use is rarely “pure”: The C program defined the gcd function in a purely functional style, even though C is mainly imperative. The Java program used some imperative code to compute the gcd, and was not completely object-oriented (integers aren’t objects). The Scheme code used sequencing to do I/O, an imperative feature. Chapter 1 K. Louden, Programming Languages
Examples of languages that are pure (mostly): Imperative: (old) FORTRAN Functional: Haskell Object-oriented: Smalltalk Chapter 1 K. Louden, Programming Languages
K. Louden, Programming Languages Language definition Syntax: the structure of a program. Usually given a formal (i.e., mathematical) definition using a context-free language. (Lexical structure - the structure of the words or tokens - uses regular expressions.) Semantics: the actual result of execution. Usually described in English, but can be done mathematically (Chapter 13). Semantics can have a static component: type checking, definition checking, other consistency checks prior to execution. Chapter 1 K. Louden, Programming Languages
K. Louden, Programming Languages Language translation Compiler: two-step process that translates source code into target code; then the user executes the target code. Interpreter: one-step process in which the source code is executed directly. Hybrids are also possible (Java). Chapter 1 K. Louden, Programming Languages
K. Louden, Programming Languages Error classification Lexical: character-level error, such as illegal character (hard to distinguish from syntax). Syntax: error in structure (e.g., missing semicolon or keyword). Static semantic: non-syntax error prior to execution (e.g., undefined vars, type errors). Dynamic semantic: non-syntax error during execution (e.g., division by 0). Logic: programmer error, program not at fault. Chapter 1 K. Louden, Programming Languages
Notes on error reporting A compiler will report lexical, syntax, and static semantic errors. It cannot report dynamic semantic errors. An interpreter will often only report lexical and syntax errors when loading the program. Static semantic errors may not be reported until just prior to execution. Indeed, most interpreted languages (e.g. Lisp, Smalltalk) do not define any static semantic errors. No translator can report a logic error. Chapter 1 K. Louden, Programming Languages
K. Louden, Programming Languages Sample Errors (Java): public int gcd ( int v# ) // lexical { int z = value // syntax - missing ; y = v; // static semantic - y undefined while ( y >= 0 ) // dynamic semantic - // division by zero { int t = y; y = z % y; z = t; } return y; // logic - should return z Chapter 1 K. Louden, Programming Languages
K. Louden, Programming Languages Language design Good, consistent set of abstractions. Tie-in to other technology (or clear single goal): C : Unix Java : Internet C++ : most efficient OO language Now also: Ease of interface with other languages and systems Good API libraries Chapter 1 K. Louden, Programming Languages