V Language, Logic and Computation: Midterm review notes
Fritz Ruehr • CS 451 • 5 April 2006
V Exam details
* Exam to be held Monday 10 April 2006 at 3:00pm in Collins 408 (normal lecture time and place)
* closed book, closed notes, no calculators or computers, please!
* lecture coverage: lecture material through Monday 3 April (parsing combinators, but not scoped variables)
* lab/homework coverage: labs 1-6 (basic Haskell, DFAs and regular languages, lists and radix-based numerals, trees and terms)
V Hand-outs and other study resources
* this guide; and most importantly your own notes from class
* labs and homeworks (especially important to look over at a later time, from a broader perspective)
* written hand-outs, charts and diagrams (e.g., on Regular Expressions)
* web materials, sample code files and other references: see the course home page
V General introduction to language
V The components of language
* language is physically linear, "spiritually" tree-like, and refers to complex, inter-woven "realities"
* syntax: the structure of phrases, their relative order and nesting
* semantics: the meaning of language
V in natural language linguistics we have:
* phonology: study of speech-sound production
* phonetics: study of low-level speech-parts (vowels, consonants, vocal stress, etc.)
* orthography: study of writing and symbol systems
* morphology: study of words, prefixes, suffixes, etc.
* pragmatics I: performance issues (limits of human memory, etc.)
V pragmatics II: hidden assumptions, global knowledge, reasonable behavior
* example: telling the whole truth ("I ate an apple." versus "I actually had a whole lunch.")
V (Object) language and meta-language
* use one language to talk about (or implement) another language (or formal systems to study formal systems)
* the language being discussed (or implemented, or studied) is the object language
* the language being used to discuss (or implement, or study) is the meta-language
* example: a Java compiler written in Haskell (Java = object language, Haskell = meta-language)
V Formal language versus natural language
V natural languages (of course) pre-date formal ones, and thus influence their structure
* the study of (natural) linguistics thus influences formal languages (but also vice versa)
V natural languages have evolved, formal languages are designed
* natural linguistics is more of an empirical science
* most designed languages are not general-purpose, but have a very restricted scope or domain
* natural languages are broader, more complex, more robust and generally more organic (less "tidy")
* formal languages are narrower, simpler, more fragile but also fully circumscribed (idealized)
* pragmatics issues for formal languages differ somewhat: issues of style & clarity, software engineering, efficiency issues
V Computer translation of formal languages
V kinds of language translators
* interpreter: parse and evaluate phrases dynamically, "on-the-fly" (may have to re-parse, etc.)
* compiler: translate whole pre-existing text statically, into another language
V structure of translators [see hand-out!]
* a sequence of steps or passes, converting one data structure to another
* lexical analysis: convert a character string to a sequence of tokens
V parsing: convert tokens to a parse tree/abstract syntax tree
* parse tree (with extra residual nodes) trimmed down to abstract syntax tree ("no extras")
* example: expressions with terms and factors for precedence, but not in AST
* static analysis: context-sensitive aspects of names, types, etc. (tree decoration and symbol tables)
* intermediate code generation: traverse tree to produce abstract instruction sequence
* general optimization: compress/convert redundant instructions, etc.
* machine-specific translation and optimization: conversion from abstract to real instructions
V Review of mathematical concepts
V Sets and set operations
* sets are characterized by membership only: no order or repetition
* finite set notation ({a,b, …}) and its limitations (hard to capture patterns)
* describing infinite sets with finite language (ex: { n | n is the sum of two squares } )
* union (), intersection (), complementation (overbar); these are standard from set theory
* cross-product (A x B): set of all pairs from A and B
* finite exponentiation (Ak): set of sequences of elements from A of length k
* Kleene star (A*): set of all sequences over A, of any length (including 0)
* power set (A): set of all sub-sets over A (including A and the empty set)
* function space (AB): set of all functions from A to B
V Cardinality issues
* sets have same cardinality (notation: |A| ) when they can be put in one-to-one correspondence
* the cardinality of infinite sets is not intuitive (ex: even numbers same cardinality as all numbers)
* "basic" infinity: cardinality of natural numbers (countable)
V "bigger" infinity: cardinality of real numbers (uncountable)
* corresponds to power set or function space over any countable set
* diagonalization argument I: establishes countability of products (zig-zag counting)
* diagonalization argument II: establishes countable < uncountable (opposite choice along diagonal)
V Relations and properties
* relations are sets of pairs or tuples (the related things)
* properties are a degenerate case for "1-ary" tuples (a subset of the original set)
* relations and properties are equivalent to boolean-valued functions on (tuples of) arguments
V properties of relations (!): reflexive, symmetric, anti-symmetric, transitive
* equivalence relation (=): ref, sym, trans
* partial order (<): anti-sym, trans
* total order: partial order with every pair comparable (i.e., either a < b or b < a)
* we can partition a set by an equivalence relation (into sub-sets of equivalent things)
V Functions
* in set theory, a function is a relation (set of pairs) with a special property (only one output for each input)
* function space operator and cardinality issues (see above)
V computable functions are those for which an algorithm (program representation) exists
* function: abstract argument-result connection (timeless)
* algorithm: general method of implementing function (getting result from argument)
* program: concrete implementation of algorithm in some language
* partial functions (may be undefined or loop forever) versus total functions (always returns a result)
V Symbols, sets and products
V "atomic" symbols
* symbols in isolation are not useful, but all come with a cultural context
V semiotics distinguishes between three kinds or aspects of signs
* (sign is a technical term for a "unit" of communication, something which can convey meaning in context)
* indices: the sign is tied in a causal way to meaning (e.g., smoke "means" fire, howling "means" a wolf)
* icons: shape or form is similar to meaning (perhaps the sign is an abstraction of apearance)
* symbols: an arbitrary (but consistently recognizable) form which gains meaning by cultural/contextual agreement
* notion of "atomicity" is relative: names often have structure, but are considered unitary for many purposes
* in mathematics, abstract atomic "individuals" have meaning only through their relation to other individuals
V sets and enumerated types
* sets of symbols are usually called alphabets
* in mathematics, finite sets without any other structure are significant only in their cardinality (number)
* in semantic terms, sets of symbols are often important for their consistent relation to (suitably related) meanings
V Issues re sets, types, domains and ranges
* thinking of meaning as a function, there is always a question of what the proper domain and range are
(Is "1012" an appropriate binary numeral? What day of the week is January 38th? etc.)
* in computer science, we try to use (static or dynamic) types as constraints to avoid "meaningless" terms
* static typing rejects meaningless terms upon construction, as not well-formed (by some rules of syntax)
* dynamic typing checks for semantic sensibility during the resolution of meaning (i.e., during evaluation)
* ideally, we have a nice crisp domain (every element is meaningful) in one-one corresp. with meanings
* but in practice, we often draw meaningful terms from a superset of representations/forms
(in the worst case, arbitrary strings over some über-alphabet)
* and in practice, there may be meanings we cannot signify directly (e.g., uncomputable functions)
* Haskell's (and other languages) enumerated types provide isolated (crisp) symbolic domains
* an ordering on set elements is often useful (think of deriving Ord class for enumerated types)
V pairs and products
* pairs can be taken as fundamental (combinations) or constructed (e.g., in set theory) in terms of their order
* pairs are necessarily represented syntactically in terms of order (one first, the other second)
V the ordering of parts of a pair and ordering of underlying elements yield a derived order
* lexicographic order: compare first parts, and only if they are equal, second parts
* we can also enumerate (count) pairs in terms of sum-and-product of the enumeration of their parts
(using e.g. the sumProd function, whose "base" argument represents the size of the less significant pair-part)
* conversion the other way (representation) can be done by using division-and-remainder (divMod)
V Numbers and Numerals
* basis for number is in quantity (cardinal) and counting (ordinal)
* in the abstract, we equate things "in number" when there exists a one-one correspondence between them
* we can represent (abstract) numbers pictorially by means of collections (a la dice) or the number line
V the number line has the advantage of suggesting the whole set of (e.g.) natural numbers at "once"
* … but it also provides a sense of their fundamental logical structure (via zero and successor)
V Peano-style naturals (built from zero and succ) are a logico-linguistic "take" on the number line
* (numbers are constructed as well-formed terms from a constant and a function/constructor)
* with each numeral we can associate the corresponding iteration (application of a function several times to a constant)
* Peano-style naturals constitute a kind of "unary notation" (but it's not a proper special case of radix/base notation)
* efficiency concerns (especially of representation, but also calculation) motivate positional notation
* positional notation is essentially an extension of lexicographic order, with multiple places
* if the symbol set (digits) are uniform across places, we can extend the notation indefinitely
(otherwise we are restricted to representing numbers as large as the product of digit sets per place)
V "evaluation" of (unfiorm base) numerals can be construed in terms of powers
* sum of (for each digit place: product of each digit value with base to power (of that place))
V but evaluation can also be construed in terms of Horner's rule, building up as we go
* from the left: add each new digit value to total, then mutliply by total by base as we proceed
* the latter technique is easily captured in terms of a fold (using sumProd)
V conversion the other way (representation) can be done by using division-and-remainder (divMod)
* this can be written in Haskell as an unfold, using divMod)
V Formal languages: structure and complexity
V Formal languages as sets of strings
* abstract notion of symbols (must support equality, i.e., be identifiable and distinguishable)
* strings are just sequences (of arbitrary but finite length) of symbols, over some alphabet
* main operation on strings: concatenation
V a (formal, string) language is just a set of strings
* i.e., a sub-set of the whole set of strings over the (implicit) alphabet
* but also a property (and thus a boolean function) over the whole set of strings
V Finite automata
* finite automata are formal models of simple machines with states (nodes) and transtitions (moves, arrows)
* FAs come in two flavors, deterministic (DFA) and non-deterministic (NFA), but they are inter-convertible
* a DFA is a 5-tuple, <Q,∑,d,q0,F> (states, alphabet, transition function, initial state, final states);
d takes a state and a symbol and returns a new state (dŒ Q x ∑ Æ Q)
* the DFA starts in the initial state, moves from state to state based on transition function;
if it ends in a final state (when the string is consumed), we say the string is accepted
* associated with each DFA is thus a set of strings (the ones it accepts), called the language of the DFA
V Regular expressions
* a regular expression (RE) has one of several forms: , e, a, R | S, R·S or R* [the · is often suppressed]
(here a is a symbol from some implicit alphabet, and R and S are regular expressions)
V the meaning of an RE is a set of strings:
* [] = {} (the empty set)
* [e] = {""} (the singleton set with an empty string)
* [a] = {"a"} (the singleton set with the one-length string containing just 'a')
* [R | S] = R » S (union of sets of strings from R and S)
* [R·S] = {xy | x Œ R, y Œ S} (concatenations of strings from R and S, one each, in that order)
* [R*] = » R^k, k≥0 (set of concatenated sequences of members from R, of any length)
* associated with each RE is thus a set of strings, called the language of the RE
V Regular languages (in the abstract)
* REs and DFAs are inter-convertible, in a way which preserves their languages
V both REs and DFAs can define only fairly simple languages: linear repetition, no nesting/coordination
* the "canonical" example of a language which they cannot define is {a^n b^n | n≥0}
* the existence of these two (and other) equivalent representations for the same "class" of languages
justifies our identifying it as a "real" entity, the regular languages
V Context-free languages
V a context-free grammar describes the structure of a language (recursively) in terms of "nested sub-phrases"
* canonical example (arithmetic expressions): E ::= N | E + E | E x E | ( E )
* numerals as a CFG: N ::= D | DN [but no nesting, so also possible as an RE: D·D*]
V formally, a context-free grammar (CFG) is a 4-tuple <S,V,T,R> (start symbol, variables, terminals, rules)
* S is a special member of V, V is a set of symbols (not appearing in actual strings), T is an alphabet of symbols (these do appear in strings) and each rule in R is a pair from V x (V»T)*
* a derivation step takes a string of symbols (V»T) and replaces some occurrence of a variable by the corresponding right-hand side of a rule in R (example: 2 + E * E => 2 + E * ( E ) )
V a string (of terminals) is generated by the grammar if there is a multi-step derivation starting at symbol S and proceeding by steps (according to the rules) until we reach that string of terminals
* example: E => E + E => N + E => 2 + E => 2 + 5
* for each derivation there is a naturally corresponding tree
V The Chomsky hierarchy
* different mechanisms/formalisms are available for describing languages (grammars, machines, etc.)
V these form a hierarchy of lesser to greater descriptive power (simple distinctions versus complex ones)
* regular:: essentially linear repetition structure (DFAs, NFAs, reg. exprs)
* context-free: tree-like structure, without contextual restrictions (CF grammars)
* ...
* general/computable: arbitrary expressible complexity (the full power of any computing formalism)
* uncomputable: by cardinality arguments (below), not all functions can be represented concretely
V Computability issues
* since the cardinality of the set of (arbitrary-length) strings over a non-trivial alphabet is countable, the cardinality of sub-sets (languages) over that set is uncountable (power-set rule, diagonalization)
* since descriptions of languages are finite strings themselves, there are only countably many of them
* therefore "most" languages (uncountably many) are not expressible or describable at all
* programs (as finite representations of functions) suffer similarly: most functions (on countable sets) are not computable, since there are uncountably many such functions
V Algebraic term languages
V Strings versus terms in formal languages
* traditional formal language theory (Chomsky-style) concentrates on the complexity of sets of strings
* we want to focus instead on the interpretation (meaning) of terms (trees derived from strings)
V Compositional meaning of terms
* we will consider evaluation or meaning of terms as functions from terms to various domains
* we distinguish standard interpretations (e.g., numbers for arith. exprs) from non-standard ones (strings, "polarities")
* meaning is compositional: meanings of terms will be defined relative to meanings of sub-terms
(i.e., meaning will be a recursive function over sub-terms)
V Term languages developed in class
* we explore several different language domains, a range of increasingly-complex language features, variations in meanings given to them, and techniques for representation of terms and meanings in Haskell
V Dimensions of domains and language features
* simple numeral structure (radix-based); a lab exercise
V arithmetic expressions with:
* literals and binary operators
* variables-as-unknowns (single or multiple)
* scoped variables (where definitions) [not on the exam]
* imperative variables [not on the exam]
* boolean expressions (literals, binary operators and variables-as-unknowns)
* regular expressions (sets of strings)
V Dimension of variation in “meanings”
* standard meanings as "atomic" domain (e.g., integers or booleans) or structured (sets of strings, below)
* single variables (unknowns) as polynomials or as (meta-language, Haskell) functions on integers
V multiple variables via functions on tuples or environments
* environment = function from variables to values; passed in to each sub-term
* scoped variables via syntactic term substitution or via evaluation and environments
V regular expression semantics
* as sets of strings (really lists of strings; but we must order them to avoid infinite searches)
* as predicates (i.e., boolean-valued functions)
V imperative variables via state transformations (to model changes over time) [not on the exam]
* state is also a function from variables to values; passed in to and out of each sub-term
* imperative variables via compilation to abstract machine code (lists of instructions) [not on the exam]
V Non-standard meanings or interpretations
V re-representation of terms as strings (prefix, infix, postfix forms; other variations possible)
* infix "x + 2 * 3 - y" versus postfix "x 2 3 * y - +"
* calculation traces (lists of terms showing successive evaluation; like a calculator tape)
* polarities (lab exercise): an abstract interpretation giving just the numeric signs of terms
V Dimension of Haskell term representations
V general issues [see also hand-out]
* "sloppiness": do we allow invalid forms relative to the language, e.g., negative numbers
* dependence on meta-language ("pure" symbolic representations are inefficient but less biased)
* flexibility: can we get from the representation form to various aspects we might want
V for numerals/numbers
* strings OR internal Haskell integers OR recursively-defined Naturals
V for operators
* strings OR abstract symbols OR functions (note: inflexible, functions don't support equality, printing)
V for term data type
V use separate constructors for each operator
* Term = Lit Int | Plus Term Term | Mult Term Term | ...
V OR, use a carrier type for operators (but bake-in references to external types)
* Term = Lit Int | Bop Opr Term Term
* Opr = Plus | Mult | ...
V OR, use type parameters of the data type to allow other choices for carriers
* Term a b = Lit a | Bop b Term Term
* ArithExpr = Term Int Opr
* BoolExpr = Term Bool LOpr ; LOpr = And | Or | ...
* the last variation (parameterized) gives the most flexibility
V Haskell as a meta-language
* knowledge of Haskell will not be tested as such on the exam, but we will use it as a meta-language
V Basic concepts
* interactive interpreter relative to external file (script) of definitions
* familiar algebraic syntax, but with fewer parentheses (e.g., "f x" rather than "f(x)")
* usual integers, floats, booleans, plus arbitrary-precision integers, rational numbers, pairs
V several simple notations for lists (represented internally using linked lists)
* [1,2,3,4] == [1..4] == 1:2:3:4:[] == 1:(2:(3:(4:[])))
* basic atomic character type ('a'); strings are lists of characters ("abc" == ['a','b','c'])
* all expressions are pure values: no imperative (updating) variables, no aliasing (shared references)
V some standard functions: length, reverse, take, drop, map, zip, zipWith, filter, foldr, foldl
* a set of useful function definitions may be supplied with the exam
V Types and typing
* all terms must have a correct type (or they will not compile); usually types are implicit (inferred)
* notation: e::t means expression e has type t
V function application associates to the left; function types (arrows) associate to the right
* f s n b == ((f s) n) b but f::String->Int->Bool <=> f::String->(Int->Bool)
* list type is written with brackets: [Int] = lists of integers
* types may contain type variables (reverse::[a]->[a]): means that type is valid for all instances
V Higher-order functions
V functions may take other functions as arguments or return them as results
* map::(a->b)->[a]->[b]
V functions of successive arguments are really just functions with function results
* (a->b)->[a]->[b] <=> (a->b) -> ([a]->[b])
V functions of successive arguments may be applied to only some of their arguments (currying)
* map f [1,2,3] but also just (map f)
* functions may also be stored in data structures, returned as results of expressions, etc.
V User-defined algebraic data types
V we can define new algebraic data types using tagged alternatives (constructors) and recursion
* Nat = Zero | Succ Nat
* List a = Nil | Cons a (List a)
* values of these types are written using the constructors as functions
* although written as functions, constructors just "hold" their arguments for later "de-construction"
V functions over these types are written with pattern-matching (usually recursive where the type is)
* length Nil = 0
* length (Cons x xs) = 1 + length xs
* convenient for representing terms, as the syntax is almost exactly that of context-free grammars
V Fold functions for semantics
* for each data type, we can define a special function called the fold for that type
* the fold takes parameters and a value of the type and returns a result computed by recursion
V fold takes one parameter for each constructor of the type, and uses it to "act" on that form
* for an atomic constructor, the parameter is a value; for constructors with arguments, it is a function
* data Foo = K1 t1 | K2 t2 | ... | Kn tn
* fold f1 f2 ... fn (K1 x) = f1 (...)
fold f1 f2 ... fn (K2 x) = f2 (...)
...
fold f1 f2 ... fn (Kn x) = fn (...)
* the fold function can be used to define concise, stylized notions of meaning
V Type classes
* type classes are similar to Java's interfaces
* syntax: class Foo a where var :: type ...
* a type being declared an instance of a type class corresponds to a Java class implementing an interface
* syntax: instance Foo a where var = defn ...
* one must show in each instance declaration how the type can be seen as a member of the class, by providing the appropriate function definitions