Lecture #1, Tuesday, January 9th ================================ - Intro to CS4400/CS5400 - Intro to Programming Languages - Intro to Racket - Side-note: "Goto Statement Considered Harmful" - Quick Intro to Racket ------------------------------------------------------------------------ # Intro to CS4400/CS5400 * General plan for how the course will go. * Administrative stuff. (Mostly going over the web pages.) ___`https://pl.barzilay.org/`___ ------------------------------------------------------------------------ # Intro to Programming Languages > [PLAI §1] * Why should we care about programming languages? (Any examples of big projects *without* a little language?) * What defines a language? - syntax - semantics - libraries (runtime) - idioms (community) * How important is each of these? - libraries give you the run-time support, not an important part of the language itself. (BTW, the line between "a library" and "part of the language" is less obvious than it seems.) - idioms originate from both language design and culture. They are often misleading. For example, JavaScript programmers will often write: function explorer_move() { doThis(); } function mozilla_move() { doThat(); } if (isExplorer) document.onmousemove = explorer_move; else document.onmousemove = mozilla_move; or if (isExplorer) document.onmousemove = function() { doThis(); }; else document.onmousemove = function() { doThat(); }; or document.onmousemove = isExplorer ? function() { ... } : function() { ... }; or document.onmousemove = isExplorer ? () => { doThis(); } : () => { doThat(); }; or document.onmousemove = isExplorer ? doThis : doThat; How many JavaScript programmers will know what this does: function foo(n) { return function(m) { return m+n; }; } or this: n => m => m+n; (x,y) => s => s(x,y); or, what seems fishy in this? --- const foo = (x,y) => bar(x,y) Yet another example: let x = ""; while (foo()) x += whatever(); How would you *expect* this code perform? How do you think it does in the reality of many uses of JS by people who are not really programmers? * Compare: - `a[25]+5` (Java: exception) - `(+ (vector-ref a 25) 5)` (Racket: exception) - `a[25]+5` (JavaScript: exception (or NaN)) - `a[25]+5` (Python: exception) - `$a[25]+5` (Perl: 5) - `a[25]+5` (C: ___BOOM___) => syntax is mostly in the cosmetics department; semantics is the real thing. * Another example: - `a + 1 > a` (Python: always true) - `(> (+ a 1) a)` (Racket: always true) - `a + 1 > a` (C: sometimes true) * How should we talk about semantics? - A few well-known formalisms for semantics. - We will use programs to explain semantics: the best explanation *is* a program. - Ignore possible philosophical issues with circularity (but be aware of them). (Actually, they are solved: Scheme has a formal explanation that can be taken as a translation from Scheme to logic, which means that things that we write can be translated to logic.) - We will use Racket for many reasons (syntax, functional, practical, simple, formal, statically typed, environment). ------------------------------------------------------------------------ # Intro to Racket * General layout of the parts of Racket: - The Racket language is (mostly) in the Scheme family, or more generally in the Lisp family; - Racket: the core language implementation (language and runtime), written in C; - The actual language(s) that are available in Racket have lots of additional parts that are implemented in Racket itself; - GRacket: a portable Racket GUI extension, written in Racket too; - DrRacket: a GRacket application (also written in Racket); - Our language(s)... * Documentation: the Racket documentation is your friend (But beware that some things are provided in different forms from different places). ------------------------------------------------------------------------ ## Side-note: "Goto Statement Considered Harmful" > A review of "Goto Statement Considered Harmful", by E.W. DIJKSTRA > > This paper tries to convince us that the well-known goto statement > should be eliminated from our programming languages or, at least > (since I don't think that it will ever be eliminated), that > programmers should not use it. It is not clear what should replace it. > The paper doesn't explain to us what would be the use of the `if` > statement without a `goto` to redirect the flow of execution: Should > all our postconditions consist of a single statement, or should we > only use the arithmetic `if`, which doesn't contain the offensive > `goto`? > > And how will one deal with the case in which, having reached the end > of an alternative, the program needs to continue the execution > somewhere else? > > The author is a proponent of the so-called "structured programming" > style, in which, if I get it right, gotos are replaced by indentation. > Structured programming is a nice academic exercise, which works well > for small examples, but I doubt that any real-world program will ever > be written in such a style. More than 10 years of industrial > experience with Fortran have proved conclusively to everybody > concerned that, in the real world, the goto is useful and necessary: > its presence might cause some inconveniences in debugging, but it is a > de facto standard and we must live with it. It will take more than the > academic elucubrations of a purist to remove it from our languages. > > Publishing this would waste valuable paper: Should it be published, I > am as sure it will go uncited and unnoticed as I am confident that, 30 > years from now, the goto will still be alive and well and used as > widely as it is today. > > Confidential comments to the editor: The author should withdraw the > paper and submit it someplace where it will not be peer reviewed. A > letter to the editor would be a perfect choice: Nobody will notice it > there! ------------------------------------------------------------------------ # Quick Intro to Racket Racket syntax: similar to other Sexpr-based languages. Reminder: the parens can be compared to C/etc function call parens --- they always mean that some function is applied. This is the reason why `(+ (1) (2))` won't work: if you use C syntax that is `+(1(), 2())` but `1` isn't a function so `1()` is an error. > An important difference between *syntax* and *semantics*: A good way > to think about this is the difference between the *string* `42` stored > in a file somewhere (two ASCII values), and the *number* `42` stored > in memory (in some representation). You could also continue with the > above example: there is nothing wrong with "*murder*" --- it's just a > word, but *murder* is something you'll go to jail for. The evaluation function that Racket uses is actually a function that takes a piece of syntax and returns (or executes) its semantics. ------------------------------------------------------------------------ `define` expressions are used for creating new bindings, do not try to use them to change values. For example, you should not try to write something like `(define x (+ x 1))` in an attempt to mimic `x = x+1`. It will not work. ------------------------------------------------------------------------ There are two boolean values built in to Racket: `#t` (true) and `#f` (false). They can be used in `if` statements, for example: (if (< 2 3) 10 20) --> 10 because `(< 2 3)` evaluates to `#t`. As a matter of fact, *any* value except for `#f` is considered to be true, so: (if 0 1 2) --> 1 ; all of these are "truthy" (if "false" 1 2) --> 1 (if "" 1 2) --> 1 (if null 1 2) --> 1 (if #t 1 2) --> 1 ; including the true value (if #f 1 2) --> 2 ; the only false value (if #false 1 2) --> 2 ; another way to write it (if false 1 2) --> 2 ; also false since it's bound to #f ------------------------------------------------------------------------ Note: Racket is a *functional* language --- so *everything* has a value. This means that the expression (if test consequent) has no meaning when `test` evaluates to `#f`. This is unlike Pascal/C where statements *do* something (side effect) like printing or an assignment --- here an `if` statement with no alternate part will just *do nothing* if the test is false... Racket, however, must return some value --- it could decide on simply returning `#f` (or some unspecified value) as the value of (if #f something) as some implementations do, but Racket just declares it a syntax error. (As we will see in the future, Racket has a more convenient `when` with a clearer intention.) ------------------------------------------------------------------------ Well, *almost* everything is a value... There are certain things that are part of Racket's syntax --- for example `if` and `define` are special forms, they do not have a value! More about this shortly. (Bottom line: much more things do have a value, compared with other languages.) ------------------------------------------------------------------------ `cond` is used for a `if` … `else if` … `else if` … `else` … sequence. The problem is that nested `if`s are inconvenient. For example, (define (digit-num n) (if (<= n 9) 1 (if (<= n 99) 2 (if (<= n 999) 3 (if (<= n 9999) 4 "a lot"))))) In C/Java/Whatever, you'd write: function digit_num(n) { if (n <= 9) return 1; else if (n <= 99) return 2; else if (n <= 999) return 3; else if (n <= 9999) return 4; else return "a lot"; } (Side question: why isn't there a `return` statement in Racket?) But trying to force Racket code to look similar: (define (digit-num n) (if (<= n 9) 1 (if (<= n 99) 2 (if (<= n 999) 3 (if (<= n 9999) 4 "a lot"))))) is more than just bad taste --- the indentation rules are there for a reason, the main one is that you can see the structure of your program at a quick glance, and this is no longer true in the above code. (Such code will be penalized!) So, instead of this, we can use Racket's `cond` statement, like this: (define (digit-num n) (cond [(<= n 9) 1] [(<= n 99) 2] [(<= n 999) 3] [(<= n 9999) 4] [else "a lot"])) Note that `else` is a keyword that is used by the `cond` form --- you should always use an `else` clause (for similar reasons as an `if`, to avoid an extra expression evaluation there, and we will need it when we use a typed language). Also note that square brackets are read by DrRacket like round parens, it will only make sure that the paren pairs match. We use this to make code more readable --- specifically, there is a major difference between the above use of `[]` from the conventional use of `()`. Can you see what it is? The general structure of a `cond`: (cond [test-1 expr-1] [test-2 expr-2] ... [test-n expr-n] [else else-expr]) ------------------------------------------------------------------------ Example for using an `if` expression, and a recursive function: (define (fact n) (if (zero? n) 1 (* n (fact (- n 1))))) Use this to show the different tools, especially: * special objects that *cannot* be used * syntax-checker * stepper * submission tool (installing, registering and submitting) An example of converting it to tail recursive form: (define (helper n acc) (if (zero? n) acc (helper (- n 1) (* acc n)))) (define (fact n) (helper n 1)) ------------------------------------------------------------------------ Additional notes about homework submissions: * Begin every function with clear documentation: a type followed by a purpose statement. * Document the function when needed, and according to the guidelines above and in the style guide. * After the function, always have a few test cases --- they should cover your complete code (make sure to include possible corner cases). Later on, we will switch to testing the whole file through it's "public interface", instead of testing each function.