#lang at-exp slideshow #| Run this slideshow by: (a) download the staged-slide library (first time only): To download from the command-line: `raco pkg install staged-slide` where `bin/raco` is in the same directory as DrRacket; you can find it with `(explode-path (find-system-path 'exec-file))` and if it's already in your path then: `(system "/usr/bin/env raco pkg install staged-slide")` (b) Then run this racket program like any other racket file: (b1) from command line: `racket [filename.rkt]`, or (b2) from inside DrRacket: Racket > Run. (c) To generate a pdf, from the commandline `slideshow --pdf [filename.rkt]` where `bin/slideshow` is found in the same directory as DrRacket. |# (require (only-in racket/draw the-color-database)) (require slideshow/code) ;(require unstable/gui/slideshow) (require slideshow/staged-slide) (require pict/conditional) (define (s title . content) (apply slide #:title title content)) ;(define p para) (define -> "→") ;(define blue (send the-color-database find-color "blue")) (define formula-color (send the-color-database find-color "blue")) (define defn-term-color (send the-color-database find-color "firebrick")) (define defn-body-color (send the-color-database find-color "sienna")) (define (formula . strs) (colorize (para " " (apply it* strs)) formula-color)) (define (formula-inline . strs) (colorize (apply it* strs) formula-color)) ; rectify a string to a pict. ; (define (to-pict blob) (cond [(pict? blob) blob] [(string? blob) (t blob)] [else (raise-type-error 'to-pict "string-or-pict" blob)])) ; Convert any number of strings-or-picts to a single pict (horizontally) ; (define (span #:combine-with [combiner hb-append] . elems) (apply combiner (map to-pict elems))) (define (define-term the-term . the-defn) (ht-append (term the-term) (it ": ") (apply term-defn the-defn))) (define (term t) (colorize (it t) defn-term-color)) (define (term-defn . defn) (colorize (apply para defn) defn-body-color)) ; accept-multiple-strings : (string -> any) -> (string... -> any) ; Accept any number of strings, append them, and then apply `f`. ; (define (accept-multiple-strings f) (λ args (f (apply string-append args)))) ; E.g. (it "hello" "there"), (it "hello" @-> "there). (define it* (accept-multiple-strings it)) (define t* (accept-multiple-strings t)) ;------------------------------------------------------------------- (s "Three useful categories" @para{Learning a programming language involves:} @define-term{Syntax The grammar rules defining a program (or fragment).} @define-term{Semantics The meaning of various programming fragments.} @define-term{Pragmatics How to @it{effectively} use language features, libs, IDEs, …} @para{All three of these are important in how easy it is to easily write high-quality software.} @para{For all categories, consider: Principle of least surprise.} ) #| Syntax examples, taken from (simplified) Java: [id-decl] ::= [modifier...] [type] [ident]; | [modifier...] [type] [ident] = [expr]; [modifier] ::= final | static | public | private | protected | synchronized int n; public double x; final private static double y = Math.sqrt(16); [method-call] ::= [expr] . [name] ( [expr...] ) In particular, note that the syntax does *not* require an object before the '.', as in `"hello".substring(2,5)` or `("hello".substring(2,5)).toUpperCase()` Although syntax tends to be very precisely defined, semantics tends to be described in English, in the language specs. And being in English, it is possible that it's ambiguous. The Algol-60 language spec had inconsistencies. |# #| Pragmatics might also include: file organization (e.g. Java class/file equivalence), how/where to best comment (e.g. javadoc, literate programming), code conventions (e.g. ending predicates with `?`, types start with upper-case, ...) how to organize tests (JUnit, or submodule named `test`, or ...) |# #| Examples which violate priniciple of least surprise: Syntax: 020 == 16 Semantics: NaN != NaN (this is actually IEEE standard, and is the correct thing to do) Pragmatics: (new HashSet()).add(null) works fine, but (new TreeSet()).add(null) throws NPE (but both implement Set<>) (Btw, Horstmann's "Object Oriented Design and Patterns" [isbn 978-0471744870] used in itec324 has several good anti-examples taken from real java libraries; the book "Java Puzzlers" is filled with examples. And I don't think Java is particularly worse than many other languages. (PHP gets honorable-mention for having extraordinarily surprising semantics.) |# (slide/staged [termlist value variable type expression] #:title "Some vocabulary" (para "Do" @it{not} "confuse the following four!" (item (ht-append @term{value} @it{: } (show @term-defn{a datum -- the fundamental piece of information that can be represented in the program} (after termlist))) (show @para{E.g. @code[37] or @code["hi"]. Values can be passed to functions, returned, stored in variables.} (after termlist))) (item (ht-append @term{variable} @it{: } (show @term-defn{an identifer which, at run time, evaluates to some particular value.} (after value) ))) (item (ht-append @term{type} @it{: } (show @term-defn{a set of values} (after variable))) (show @para{E.g. Java's @tt{short} = {-32768,..., -1,0,+1,+2, ..., +32767}.} (after variable))) (item (ht-append @term{expression} @it{: } (show @term-defn{a piece of syntax which evaluates to some particular value.} (after type) )) (show @para{E.g. @tt{3+4*5} or @tt{sqrt(16)}.} (after type))) )) #| So the syntax of a Java assignment statement (sep. from init) is: [var] = [expr]; This includes: x = 17; x = y; x = y+4; x = Math.sqrt(y+4); In all cases, the bit between `=` and `;` is an expression. Note that really both "+" and "sqrt" are functions: sqrt : double -> double + : int, int -> int compare notation to math/calc: f : R -> R |# (s "Some vocabulary (cont.)" @item{@define-term["literal"]{a value which literally appears in the source-code.} E.g. Java @tt{37} or @tt{045} are both literals representing the value 37, which is of @it{type} @tt{int}. And @tt{37.}, @tt{37d}, @tt{37e0} are each literal @tt{double}s. (But @tt{pi} is not, nor @tt{n+m}.) } @para{(We @it{will} often conflate a literal with the value it represents, and only say ``literal'' when we're emphasizing that we're dealing with syntax.)} ) (slide/staged [q1 a1 q2 a2 q3 a3 q4 a4 q5 a5] #:title "trivia: Interning Java string-literals" @para{Literals occur in the source-code text, and can be processed at compile-time. In Java, string literals are ``interned'': If the same string-literal occurs twice, the the compiler is smart enough to only make one object(*), and use the same reference in both places. } @vl-append[ (show (span @tt{"Cathay" == "Cathay" } (show @tt{ // true (!)} (> stage 5))) (> stage 4)) (show (span @tt{"Cathay".substring(3) == "hay" } (show @tt{ // false} (> stage 3))) (> stage 2)) (show (span @tt{"Cathay".substring(3).equals("hay")} (show @tt{ // true} (> stage 1))) (> stage 0)) ] @para{Morever: string-literals with @tt{+} are computed at compile-time. } @vl-append[ (show (span @tt{"Cat".concat("hay") == "Cathay"} (show @tt{ // false } (> stage 7))) (> stage 6)) (show (span @tt{"Cat" + "hay" == "Cathay" } (show @tt{ // true (!)} (> stage 9))) (> stage 8)) #| int n = 35; "hmm" + 35 == "hmm35" "hmm" + n != "hmm35" |# ] (scale @t{(*) This optimization is only safe because Java strings are immutable.} 0.6) ) (s "typing: when?" @define-term{statically-typed At compile-time, the types of all declared names are known.} @para{Can be provided by programmer and checked by type-system, or inferred by the language (ML, Haskell). (C# allows simple @tt{var n = 5;} and infers @tt{n} ∈ int).} @define-term{dynamically-typed Language knows the type of every value.} @para{But a variable might hold values of different types, over its lifetime. php, javascript, racket. Each value (incl. primitive types) includes some extra “tag” bits, indicating its type.} ) (s "static vs dynamic trade-offs" @para{todo -- make this slide, Ian.} @para{Sound, but not complete: @code{int foo() { if (true) return 17; else return "unreachable"; }} will never ever lead to a type-error, but Java's type-system will still reject it. } @para{@code{str += (str.charAt(0)=='\n') ? "
" : str.charAt(0);} is sensible, but Java's type-system will complain: What is the @it{type} returned by the conditional-expression? Sometimes @code{String} but sometimes @code{char}, so type-system rejects this even though @code{+=} makes perfect sense in both cases. } ) (s "typing: other approaches" @define-term{duck typing Care about an object having a field/method, not any inheritance.} @para{E.g. javascript} @define-term{untyped} @para{E.g. assembly} @define-term{type-safe Any type error is caught (either dynamically or statically).} @para{Note that C is not type-safe, due to casting. Java's casting @it{is} type-safe(*) — a bad cast will fail at run-time.} (scale @para{(*) Actually, Java generics + casting @it{can} bypass type-safety, due to type-erasure. @tt{:-(}} 0.6) ) (s "typing: strong/weak/non" @t{These terms are often used in different ways:} @define-term{strongly typed @vl-append{@t{no/few implicit type conversions,}@span{@it{or} statically typed}}} @define-term{weakly typed / untyped @vl-append{@t{many implicit type conversions,}@span{@it{or} dynamically typed}}} @para{Consider Java @tt{Math.sqrt(16)}, and Java vs php @tt{20+30+"40"}. } ; Java @tt{40+60+"50"} @tt{""+40} php @tt{50+"60huh"}, or @tt{50+"hmm60"}. ; Recall: Pragmatics, and principle of least surprise. @para{Cf. SQL (each column strongly-typed) vs SQLite (may attempt type-conversion, but will allow storing any type in a column).} @para{Implicit conversions are one way "scripting" languages are more lightweight.} ) (slide/staged [initial cross trans] #:title "Compiling" @item{A @term{compiler} is a function @formula{compile : source-code → machine-code} The resulting machine-code, when executed, runs the program which produces a resulting value. } @show[@item{A @term{cross-compiler} is just @formula-inline{source-code → machine-code} where the machine-code produced be for a different platform than the one the compiler is running on. (A boring and archaic distinction.) } (> stage 1)] @show[@item{A @term{transcompiler} is @formula-inline{source-code → source-code}, so “compile Ada into javascript” is sensible. Machine code is just one example of an target-language, so this subsumes both previous terms. } (> stage 2)] @para{“Correctness”: the result-code has identical semantics to source-code.} ) (slide/staged [initial defn-interpret interpret-consequence compile-consequence trade-off] #:title "Compiling vs interpreting (cont.)" @item{@formula{compile : source-code → source-code} Btw, this general formulation is what people typically mean by “compilation”.} @show[@item{An @term{interpreter} is a function @formula{eval : expr @-> value} which evaluates an expression, producing a result.} (> stage 1)] @show[@item{Interpreted code: CPU runs the op-codes interpreter; it looks at the source-expression as data, updating internal state appropriately.} (> stage 2)] @show[@item{Compiled code: CPU runs the op-codes of the desired program directly.} (> stage 3)] @show[@item{Compiled code: probably faster, but platform-specific.} (> stage 4)] ) (s "Compiling vs Interpreting (cont.)" @para{The distinction is practical, but not fundamental. You can even view CPUs as interpreters @;{they read compiled-code as data (from memory), and update their internal state accordingly.} for for compiled-code (!) — they look at the op-codes as data, updating the CPU's state appropriately. @;{if html+css is a program, then the browser is an interpreter.} } @item{A compromise: compile to @term{byte code}; then interpret that byte code. Trades off speed @it{vs.} platform-dependence. (See also: @term{JIT}.)} )