LMU ☀️ CMSI 585
PROGRAMMING LANGUAGE FOUNDATIONS
Practice

Reinforcement Questions

Do you like spaced repetition learning? Have you used Anki or Quizlet? Whether or not spaced repetition works for you, periodically working on flash-card like questions can be a lot of fun, and just may help you retain information. Here are a few problems tied to the course material. Visit them periodically, and feel free to use them in your own spaced repetition learning practice!

Most of the reinforcement questions here deal with language-independent concepts. You should also practice with the language-dependent questions as well.

Warmup

  1. What are the learning objectives of this course?
    Mastery of language-independent programming concepts;
    Familiarity with new programming languages;
    The ability to write formal syntactic and semantic definitions of programming languages;
    Increased mathematical maturity.
  2. What are the concerns of syntax, semantics, and pragmatics?
    Syntax—structure;
    Semantics—meaning;
    Pragmatics—usage.

The Study of Programming Languages

  1. What professional benefits can you gain from studying programming languages?
    Better expressive capacity;
    Better technical decision making;
    Ability to learn new languages more easily;
    Better productivity;
    Ability to create new languages;
    New ways of thinking;
    Ability to create tools to manipulate language and knowledge.
  2. Programs are a form of ________________ just like novels, short stories, technical manuals, wikis, screenplays, essays, dialogues, articles, and poetry.
    Literature, writing, or written expression.
  3. “If statements” are a form of ________________ and “while statements” are a form of ________________.
    conditional execution; iteration.
  4. What programming language did Alan Kay demo in his 2013 Video Programming Languages and Programming?
    I’m not telling you. Watch the video. It is worth your time. If you watched the video, you know.
  5. What are three main aspects of the study of programming languages?
    (1) Learning specific languages, (2) Learning language-independent concepts, and (3) Learning mathematical formalisms for the description of languages.
  6. When studying individual languages what we must always keep in mind?
    The reason why the language was designed, which is often a particular problem domain for which it was intended to operate in.
  7. Name at least five conceptual areas in the study of programming languages.
    Possible answers include: Binding, Scope, Evaluation, Control Flow, Typing, Procedural Abstraction, Data Abstraction, Modularity, Concurrency, and Metaprogramming. There may be others.
  8. What are three of the most popular programming languages created in the 1950s? What was the primary domain of each?
    Fortran for scientific computation, Lisp for artificial intelligence, and COBOL for business.
  9. What “revolution” in programming characterized the 1970s?
    Structured Programming.
  10. What programming paradigm started taking over the world in the 1980s?
    Object orientation.
  11. What “movements” brought back interest in functional programming in the early 21st century?
    (1) The rise of multicore processors, and (2) the rise of big data.
  12. In Bret Victor’s The Future of Programming, what are the four “predictions” his 1973 persona predicted for 2013 that never really took hold?
  13. Syntax deals with ________________ and semantics deals with ________________.
    structure; meaning.
  14. Is it for syntax or for semantics that formal methods are much easier and universally applied?
    Syntax.
  15. What are some concerns of “pragmatics” in programming languages? Name at least five.
    Suitability for a given application domain; comparison to other languages; expressiveness; performance capabilities; idioms; environments; implied execution model; standard library; ecosystem. (There may be others, but these are the main ones. If you know of another major concern, do let me know.)
  16. Name at least five technical criteria on which to evaluate programming languages.
  17. Name at least three non-technical criteria on which to evaluate programming languages.

Theories of Computer Science

  1. What exactly is a theory, in the context of art and science?
    An organized body of knowledge with explanatory and predictive powers.
  2. Why are theories important?
    They provide us with a vocabulary to communicate ideas, test hypotheses, and generate new knowledge.
  3. What are the four major computation theories and what are they concerned with?
    1. Language Theory: expressing computations
    2. Automata Theory: carrying out computations
    3. Computability Theory: What can and cannot be computed?
    4. Complexity Theory: Resources required for certain computations
  4. What is a language, in formal language theory?
    A set of strings over some finite alphabet.
  5. What is the lambda calculus seemingly most concerned with representing and manipulating?
    Functions
  6. Who came up with the idea of formalizing computing via the Lambda Calculus?
    Alonzo Church
  7. Who came up with the idea of formalizing computing via the Turing Machine?
    Alan Turing
  8. What was Turing trying to do when he realized he would have to do no less than formally define the very notion of an algorithm (or computational process)?
    He was trying to show Hilbert’s Entscheidungsproblem did not have a solution
  9. On what did Turing more-or-less base his fundamental idea of how a formal (abstract mechanical) computing device should work?
    (Human) computers, calculating on paper by moving through mental states looking at symbols, adding new symbols, and erasing existing ones.
  10. What are the main components of a Turing Machine?
    1. A control unit made up of states and a transition relation, and
    2. An infinite one-dimensional tape with cells that can each hold a single symbol or be blank.
  11. Usually, a Turing machine just rewrites its input into an output. But sometimes, all we want to compute is the answer to a YES-NO question. In this case, how does the Turing Machine announce its answer?
    It says YES by entering an ACCEPT state with no outgoing transitions, and answers NO by getting into a non-ACCEPT state with no outgoing transition for the current symbol. It is possible for the machine to loop forever on a given input, in which case it is said to give no answer.
  12. Answering a YES-NO question on a Turing Machine is isomorphic to ________________ a language.
    Recognizing
  13. Why is Turing’s discovery/invention of the Universal Turing Machine so profound?
    It means that truly general-purpose, programmable computing machines exist (i.e., we don’t need specialized machines for all computations).
  14. How can we prove there are functions that cannot be computed?
    By diagonalizing over an assumed enumeration of all functions.
  15. What does the Church-Turing Thesis say?
    That the Turing Machine lines up exactly with our notion of what is intuitively computable.
  16. What is the difference between deciding and recognizing?
    A TM decides a language if it always halts with a YES or NO answer. A TM recognizes a language if it always correctly answers YES for strings in the language, but it may or may not halt with a NO for non-members.
  17. What are the language classes P and NP?
    P is the set of languages decidable in polynomial time on a deterministic TM; NP is the set of languages decidable in polynomial time on a nondeterministic TM.
  18. Does P = NP?
  19. Did Scott Aaronson really write “everyone who could appreciate a symphony would be Mozart”? How does he feel about writing that these days?
    He did indeed write that but he regrets writing it now.
  20. What are some variations we can make to Turing Machines that possibly affect complexity (if not computability)?
    Randomized machines, probabilistic machines, quantum machines.
  21. How does Grady Booch distinguish the work in material domains (physics, chemistry, biology, etc.) from that of computer science?
    Scientists in material domains observe the cosmos and reduce it to simple principles; computer scientists start with simple principles and from them create new worlds bound only by the imagination.

Mathematics of Computer Science

  1. What are some things that mathematics deals with?
    Quantity, structure, change, chance, causality, and space.
  2. Was math invented or discovered?
    Probably both.
  3. What are some of the main branches within mathematics?
    Foundations, algebra, analysis, discrete, geometry, number theory, topology, applied mathematics.
  4. What are some characteristics of mathematical reasoning? Name at least two.
    It works with abstractions and patterns, not just with concrete objects;
    It is often done by pushing symbols around.
  5. What are three theories that claim to be able to formalize “all of mathematics”?
    Set Theory;
    Type Theory;
    Category Theory.
  6. Why are sets so effective at formalizing mathematics?
    A set expresses the very fundamental notion of an object having a property.
  7. Why isn’t $\{ x \mid x \not\in x \}$ considered a set?
    Considering it a set ruins everything because if it were a set, assuming it was a member of itself would imply it was not and assuming it was not a member of itself would imply that it was. Since we desire set membership to be well-defined (either yes or no for any set and any candidate element), we must consider this not to be a set.
  8. How is set subtraction $A-B$ defined?
    $\{x \mid x \in A \wedge x \notin B\}$
  9. What is $\mathcal{P}(\{a,b,c\})$?
    $\{\varnothing, \{a\}, \{b\}, \{c\}, \{a,b\}, \{a,c\}, \{b,c\}, \{a,b,c\} \}$
  10. What is $\bigcup \{ \mathbb{Q}, \mathbb{R}, \mathbb{N}\}$?
    $\mathbb{R}$
  11. What is $\bigcap \{ \mathbb{Q}, \mathbb{R}, \mathbb{N}\}$?
    $\mathbb{N}$
  12. What is $\varnothing \times \varnothing$?
    $\varnothing$
  13. What is $\{1,2\}^*$?
    $\{(), (1), (2), (1,1), (1,2), (2,1), (2,2), (1,1,1), (1,1,2), (1,2,1), (1,2,2), (2,1,1), \ldots \}$
  14. What is the difference between “asymmetric” and “antisymmetric”?
    An asymmetric relation can never have a member of the form $(x,x)$.
  15. What three properties characterize a partial order?
    Reflexivity, antisymmetry, transitivity.
  16. Let $R_1 = \{(3,1),(9,8),(2,5)\}$ and $R_2 = \{(8,8),(10,2),(1,7)\}$. What is $R_1 \circ R_2$?
    $\{ (10,5) \}$
  17. How do you write “let $x = 8$ in $2^x - 1$” without the “let”?
    $(\lambda x. 2^x - 1)(8)$
  18. Let $f = \lambda x. 3x + 5$. What are $f^{-1}$, $f^0$, $f^1$, and $f^2$?
    $f^{-1} = \lambda x. \frac{x-5}{3}$
    $f^0 = \lambda x. x$
    $f^1 = \lambda x. 3x + 5$
    $f^2 = \lambda x. 9x + 20$.
  19. Suppose $f = \lambda x. x^2$ and $g = \lambda x. x + 3$. What are $f \circ g$ and $g \circ f$?
    $f \circ g = \lambda x. (x + 3)^2$
    $g \circ f = \lambda x. x^2+3$
  20. Suppose $f = \lambda x. 5x + 2$ and $g = f[2/1]$. What are $f(8)$ and $f(0)$?
    $f(8) = 42$
    $f(1) = 2$
  21. How do you write the function that maps "x" to 21, "y" to 8, "z" to 55, and every other input to 0?
    $(\lambda x. 0)[21/\texttt{"x"}][8/\texttt{"y"}][55/\texttt{"z"}]$
  22. What kind of number is this: $a + bi + cj + dk$?
    A quaternion.
  23. How do you conventionally write $5 \uparrow\uparrow 3$?
    $5^{5^5}$
  24. What is $\log_2 1024$ and why?
    $10$, because $2^{10} = 1024$

Logic and Computer Science

  1. What is the difference between deduction and induction?
    Deduction is determining a conclusion from premises that is guaranteed to be true if the premises are all true; induction is determining a conclusion that is evidenced by the premises but not necessarily true, even if the premises are true.
  2. What is the difference between $A \supset B$ and $A \rightarrow B$?
    The former has no notion of causality; it just simply says there is no way $A$ can be true while $B$ is false. It does not say that the truth of $A$ causes $B$. The latter says that.
  3. How do you represent the object “The president of Mexico” in common logic notation?
    $\iota p. P m p$, where $m$ is Mexico and $Pxy$ means “the president of $x$ is $y$.”
  4. How do you represent the object “At some point in the past, Juliet believed that Romeo might someday forever live in Canada”?
    $\mathbf{P}(\mathscr{B}_j\lozenge\mathbf{F}\mathbf{G}(Lrc))$, where $j$ is Juliet, $r$ is Romeo, $c$ is Canada, and $Lxy$ means $x$ lives in $y$.
  5. What is prepositional logic good for, if anything?
    It is too weak to be useful for much really, but its simplicity (1) makes it a good introductory logic for learners, and (2) is an example of a system that is both sound and complete.
  6. What characterizes classical logic?
    Bivalence, excluded middle, non-contradiction, monotonicity of entailment, commutativity of conjunction, dualities of conjunction/disjunction and of universal/existential quantification.
  7. What is intuitionistic logic most concerned with?
    Proof, rather than truth.
  8. How are validity and satisfiability related?
    If something is valid then it is satisfiable, but not the other way around. (Similarly, if something is unsatisfiable, then it is invalid, but not the other way around.)
  9. Give an example of a valid statement of (classical) prepositional logic.
    $p \vee \neg p$
  10. Give an example of a invalid statement of (classical) prepositional logic.
    $p \wedge \neg p$
  11. Give an example of a satisfiable statement of (classical) prepositional logic that is neither valid nor invalid.
    $p \supset \neg p$
  12. What is a theorem in formal logic?
    A formula that is an axiom or that is derived from axioms via inference rules.
  13. What does it mean for a logistic system to be sound?
    Every theorem is true.
  14. What does it mean for a logistic system to be complete?
    Every true formula is a theorem.
  15. What does it mean for a logistic system to be consistent?
    No two theorems have contradictory truth values.

Formal Language Theory

  1. What is a string, in language theory?
    A finite sequence of symbols over a given alphabet.
  2. What is a language, in formal language theory?
    A set of strings over some finite alphabet.
  3. How do we denote the concatenation of two languages, $L_1$ and $L_2$?
    $L_1L_2$
  4. What is the difference between $\varnothing$ and $\{ \varepsilon \}$?
    The former is the language of zero strings; the latter is a language with one string, namely the empty string.
  5. For language $L$, what are $L\varnothing$ and $L\{\varepsilon\}$?
    $\varnothing$ and $L$.
  6. What are variables in a grammar?
  7. In what sense does a grammar define a language?
  8. What does it mean for a grammar to be ambiguous?
  9. What language does the grammar s = ε | "(" s ")" | "[" s "]" | "{" s "}" | s s define?
    The language of properly nested and balanced brackets.
  10. What is a context-free grammar?
  11. What is a right-linear grammar?
  12. What is a type-1 grammar?
  13. What is the difference between a generative and an analytic grammar?
  14. Arrange R, FINITE, RE, DCFL, CFL, and REG in subset order.
  15. What is the language class BPP?
    The set of languages that can be decided a probabilistic TM in polynomial time such that the probability of its answer being correct is $\geq$ 2/3.
  16. Arrange NP, EXPSPACE, EXPTIME, PSPACE in subset order.
  17. What is $RE \cap \textrm{co-}RE$?

Syntax

  1. What is syntax and how does it differ from semantics?
    Syntax is a specification of the structure of a language, while semantics specifies its meaning.
  2. What are some spectra of syntactic forms in common programming languages?
    Pictures vs. text
    Symbols vs. words
    Whitespace significance vs. insignificance
    Indentation vs. curly braces vs. ends vs. parentheses
    Prefix vs. infix vs. postfix
  3. What does a syntax diagram for a function call look like, assuming the function being called must be a simple identifier?
  4. What are entities and identifiers?
    An entity is something that is manipulated by a program, such as a variable, constant, function, or literal. An identifier is a name you can bind to an entity.
  5. What is the difference between the lexical and phrase syntax?
    The lexical syntax defines how individual characters are grouped into words, called tokens (such as identifiers, numerals, and punctuation); the phrase syntax groups words into higher level constructs (such as expressions and statements). These words can be separated by whitespace.
  6. Why do grammars make a distinction between lexical and phrase syntaxes? Give two reasons.
    1. To avoid littering nearly every rule with whitespace sequences.
    2. To conceptually simplify the syntax, breaking it down into two manageable parts.
  7. How do we, in the grammar notation used in class as well as in Ohm, distinguish lexical and phrase variables?
    Lexical variables begin with a lowercase letter; phrase categories begin with an uppercase letter.
  8. What exactly is a token?
    A primitive element for a grammar’s phrase structure, e.g., a keyword, identifier, numeric literal, boolean literal, simple string literal (without interpolation), or symbol.
  9. What happens during tokenization? What kinds of language constructs are “eliminated” in this process?
    Characters in the source code are grouped into tokens and comments and spaces between tokens are dropped. (Spaces within string literals are kept of course).
  10. What does it mean for a grammar to be ambiguous? Answer in precise, technical terms.
    A grammar is ambiguous if there exists a string that has two distinct derivation trees in the grammar.
  11. How is operator precedence captured in a grammar? Give an example.
    We use multiple syntactic categories for expressions. The classic example, which gives addition lower precedence than multiplication, is:
      Exp  → Exp addop Term
           | Term
      Term → Term mulop Factor
           | Factor
    
  12. What kinds of rules about program legality cannot be captured in a Ohm or context-free grammar? Find as many as you can.
    • Type checking
    • Redeclaration of identifiers within a scope
    • Use of an undeclared identifier
    • Matching arguments to parameters in a call
    • Access checks (public, private, etc.)
    • Identifiers must be used in all paths through their scope
    • A return must appear in all paths through a function
    • Pattern match exhaustiveness
    • In a subclass, abstract methods must be implemented or declared abstract
    • All private functions must be called within a module
  13. What are some advantages of hand-crafted parsers over parsers generated by a machine?
    Hand-crafted parsers and be faster and give much more precise error messages.
  14. What are some applications and libraries that can generate parsers?
    YACC, Bison, ANTLR, Ohm
  15. What is a reserved word?
    A token that has the same lexical structure as an identifier but is not allowed to be used as one.
  16. What kinds of rules about program legality can in principle be captured in a context-free syntax, but are generally not so captured in practice because the the required complexity of the rules?
    • Requiring break and continue in a loop (as this would require difference classes of statements)
    • Requiring return in a function body (as this would require difference classes of statements)
  17. In principle, can you capture the notion at all integer literals (in a hypothetical language) must be between -2147483648 and 2147483647? If so, why don’t we?
    Yes, you certainly can, but the resulting specification is ugly af and really hard to decipher and understand.
  18. Draw the concrete syntax tree for the expression 8 * (13 + 5), assuming a grammar with categories named Expression, Term, Factor, Primary, and num.
          Expression
              |
            Term
          /   |   \
     Term     *    Factor
       |         /   |    \
    Factor    (  Expression  )
       |         /     |  \
    Primary Expression +  Term
       |         |         |
      num      Term     Factor
                 |         |
              Factor    Primary
                 |         |
              Primary     num
                 |
                num
    
  19. Give a simple rule that illustrates the difference between the / operator of a PEG and the | operator of a context-free grammar. (Hint: show a case in which the language defined by two “look-alike” grammars, one PEG, one CFG, are different.
    In a PEG, A ← "a" / "ab" only matches "a", while the similar CFG defines the language {a, ab}.
  20. Is indentation-sensitivity context-free?
    No
  21. What is a concrete syntax?
    A precise specification of which strings (sequences of characters) are structurally legal programs.
  22. Draw the abstract syntax tree for the expression 8 * (13 + 5).
        *
      /   \
    8      +
          /  \
        13    5
    
  23. What kinds of constructs appear in a concrete syntax but not in an abstract syntax? See how many items you can find.
    • Punctuation and delimiters
    • Intermediate expression categories, e.g. Exp1, Exp2, Term, Factor, ...
    • INDENT and DEDENT tokens
  24. Given the expression 8 * (13   + 5), how many source code characters are there? When tokenized, how many tokens are there? How many nodes are there in the abstract syntax tree?
    14 characters, 7 tokens, 5 nodes.
  25. What kind of formal notation can capture an abstract syntax?
    A tree grammar.
  26. What are some notations that have been used to specify the syntax of programming languages?
    BNF, EBNF, ABNF.

Semantics

  1. What is the difference between syntax and semantics?
  2. Why do most language definitions provide a formal syntax but not a formal semantics?
    Syntax is normally context free and very easy to mathematically formalize; semantic definitions often feature quite a few ad-hoc constraints and lots of contextual information which is more clunky to formalize.
  3. What are the three most popular approaches to defining a formal semantics?
    Denotational, operational, and axiomatic.
  4. What are denotational semantics, operational semantics, and axiomatic semantics?
  5. What is the difference between structural operational semantics and natural semantics?
  6. What is the debate between what to call the language definition rules that cannot be captured in a context-free formalism?
    Some people call it context-sensitive syntax and others call it static semantics.
  7. What is the difference between static semantics and dynamic semantics?
    Static semantics deals with meaning we can infer and check just by looking at the program itself (i.e., before the program is run, or “at compile time”); Dynamic semantics deals with the meaning of the program in terms of its effects at run time.

The Lambda Calculus

Operational Semantics

Denotational Semantics

Programming Language Classification

  1. What are some of the major types of programming languages?
  2. What are ways that we normally distinguish “system languages” from “application languages”?
  3. What are the “two kinds” of languages identified by Ousterhout? Suggested by Kay? Joked about by Stroustrup?
  4. What is the difference between imperative and declarative programming?
  5. What characterizes the structured programming paradigm?
  6. What characterizes the object-oriented programming paradigm?
  7. What characterizes the functional programming paradigm?

Programming Language Concepts

  1. Name at least five conceptual areas in the study of programming languages.
    Possible answers include: Binding, Scope, Evaluation, Control Flow, Typing, Procedural Abstraction, Data Abstraction, Modularity, Concurrency, and Metaprogramming. There may be others.

Binding

  1. What is a binding? What, specifically is getting bound to what?
  2. What is the difference between a declaration and a definition?
  3. What is an assignable? How does assignment differ from binding?
  4. Distinguish aliasing from polymorphism.
  5. Show how pointers give rise to aliasing.
  6. What are static storage, stack storage, and heap storage, and what goes in each?
  7. What is it called in C if an entity outlives all bindings to it? What happens in Java, Go, or Python when an entity outlives all bindings to it?
  8. What is it called in C if a binding outlives its bound entity?
  9. How does Python prevent a binding from outliving the entity it is bound to? How does Rust prevent this from occurring?
  10. What is a free variable?
  11. What is the difference between deep binding and shallow binding?
  12. What is meant by the scope of a binding?
    The region of the program text in which a binding is in effect.
  13. What is the technical term for a binding hiding a previous binding with the same name?

Scope

  1. What is the difference between static and dynamic scope?
  2. What would this program output under (a) static scope rules and (b) under dynamic scope rules?
      var x = 2;
      function f() { print x; }
      function g() { var x = 5; f(); print x; }
      g();
      print x;
    
  3. What does this script print under (a) static scope rules and (b) dynamic scope rules?
      var x = 1;
      function f() {return x;}
      function g() {var x = 2; return f();}
      print g() + x;
    
    (a) 2, (b) 3
  4. Why has no (major) language combined static scope and shallow binding?
  5. What is the temporal dead zone?
  6. Distinguish Python, JavaScript, and Ruby's approaches to updating non-local variables.
  7. What is the difference between the way JavaScript and Rust handle the sequence:
      let x = 3;
      let x = 3;
    
    JavaScript disallows this ("Identifier 'x' has already been declared") while Rust makes two distinct variables.
  8. What programming language feature practically destroys the sole argument in favor in dynamic scope?
    Default parameters
  9. What are the two main approaches to implementing dynamic scope, and what are the advantages of each?
    (1) The association list, which is efficient when entering and leaving scopes, and (2) the central reference table, which performs lookup efficiently.

Expressions and Statements

  1. What is the difference between an expression and a statement?
  2. What is operator precedence? What does is mean for operator O1 to have higher precedence than operator O2? Give a precise example.
  3. What is operator associativity? What does is mean for an operator to be left aoociative? Right associative? Non associative? Give precise examples for each.
  4. Evaluate, if possible, the expression in -2**2 in JavaScript and Python. Explain why the evaluation produced the value it did in each language.
  5. Why would a language define an evaluation order for expressions? Why would it choose to leave the evaluation order undefined?
  6. How can the expressions a+(b-c) and (a+b)-c produce different results?
  7. What is a short-circuit operator?
  8. What are Lvalues and Rvalues?
  9. What does the following script output under lazy evaluation? Under eager evaluation?
      var x = 5
      function f() { x = x * 3 }
      function g() { x = x * 5 }
      function h(a, b) { return a + x }
      print(h(f(), g()))
    
  10. How is a macro different from a function?
  11. What is an expression-oriented language?
    One in which every (or nearly every) construct we think of as a statement, such as an assignment, block, if, or while, is actually an expression.
  12. How did Go “fix” the most uninutitive aspect of the C (and inherited by C++, JavaScript) switch-statement?
    It made the execution of each case end the execution of the switch statement, unless the keyword fallthrough was added.
  13. Why is it that the simple act of doing an operation 10 times so easy in Ruby but so annoyingly complex in C-like languages?
  14. What is tail recursion and why is it useful?
  15. What is the non-deterministic statement of Go? Of Erlang?

Types

  1. What is the algebraic definition of a type?
  2. What does it mean for a type to be extra-lingual?
  3. How does one query the type of an expression at run time in JavaScript? Python? Ruby?
  4. What are the main differences between a type and a class?
  5. What is a mixin?
  6. Why to languages like Ruby have a single type Array but in other languages like Java, Rust, and Swift there exist many parameterized array types?
  7. What is an eqtype? A comparable type? A bounded type?
  8. What is a monoid?
  9. Type checking is often concerned less with whether two types are identical, but rather when elements of one type T1 can be assigned to an Lvalue constrained to be of type T2. In what situations is this check made?
    (1) variable initialization, (2) assignment, (3) passing arguments to parameters, (4) returning from a function.
  10. What are three primary situations in which a type A is compatible with type B?
  11. What is the difference between type conversion and type coercion?
  12. In the conditional expression x ? y : z of a typical statically-typed language, what type checking and inference rules would a compiler be required to enforce?
    Checks: the type of x must be boolean and the types of y and z must be compatible. Inference: the type of the entire expression is the least general type of both y and z.
  13. In what sense is the type inferencing capabilities of ML and Haskell more powerful that that on Go, Rust, Java, and Swift?
  14. Why (do you think) does Java not infer the types of parameters in normal methods but does infer them for lambdas?
  15. What is the difference between strong typing and weak typing?
  16. What is the difference between static typing and dynamic typing?
  17. Classify the following languages as either Static+Strong, Static+Weak, Dynamic+Strong, or Dynamic+Weak: C, JavaScript, Java, Python.
  18. Why is the combination of static typing plus weak typing the worst of the four possible combinations?
  19. I couldn't find in the definition of C whether the language employs shallow or deep binding. Why?
  20. What are covariance, contravariance, and invariance?
  21. What is a dependent type?
  22. How are rational types different from integers or floats? Why are they needed?
  23. Given the types A = {1, 2, 3} and B = {'a', 'b'}, what are the types A+B and A×B?
  24. Untagged product types are usually called ________________. Tagged product types are usually called ________________. (There are two good answers for the latter; one starting with “s” and one starting with “s”.
  25. Tagged sum types are often called ________________.
  26. What is the most popular language with untagged sum types?
    TypeScript.
  27. Is an option type a kind of sum type? Why or why not?
  28. In what way is a struct like a dictionary? In what why are they different?
  29. What does it mean for a struct or dictionary to be ordered?
  30. What mechanism do many languages use to avoid unions?
  31. What is the difference between an array and a list?
  32. If someone talks about static arrays and dynamic arrays, what are they probably referring to?
  33. What is the difference between a string and a symbol?
  34. How do Go and Rust differ (if at all) in their interpretation of the length of a string?
  35. What are pointers for?
  36. Even though Java and JavaScript don’t have explicit pointers, knowledge about references is crucial, why?
  37. What is a memory leak?
  38. What is a dangling pointer?
  39. How do C++, Rust, and Swift deal with their lack of tracing garbage collection?

Subroutines

  1. What is the difference between a subroutine and a coroutine?
  2. What is the difference between a parameter and an argument?
  3. What are the two main ways of associating an argument with a parameter?
  4. What is a default parameter?
  5. What does it mean for a subroutine to be variadic?
  6. What is a rest parameter?
  7. JavaScript and Erlang functions are defined with something more sophisticated that simple parameters. What is this powerful concept and how is it more sophisticated?
  8. Explain the three semantic mechanisms for argument passing (in, out, and in-out).
  9. Explain the following five pragmatic mechanisms for argument passing (value, value-result, reference, name, pure-aliasing).
  10. Why have some languages created “generic” subroutines? Isn’t this whole concept unnecessary given the more general concept of parameterized and dependent types? Why or why not?
  11. What is a closure?

Functional Programming

  1. What does functional programming try to eliminate or at least minimize and completely isolate?
  2. What are the three big advantages to removing side-effects?
    1. Thread safety
    2. More opportunities for compiler optimizations
    3. Ease of proving code correct
  3. Why do languages that try to be as functional as possible not have for-loops? What functions does one use instead?

OOP

  1. Where did the original idea of object-oriented programming come from?
  2. Alan Kay said, in a letter to Stefan Ram, “OOP to me means only ________________, local retention and protection and hiding of ________________, and extreme ________________.”
  3. The two major approaches to OOP are characterized by ________________ and ________________. Which of the two is more associated with Plato and why?
  4. What might have caused the proliferation of getter and setter methods in languages like Java and C# that purport to be object oriented? What are the arguments as to why getters and setters are evil?

Concurrency

  1. How does concurrency differ from parallelism?
  2. A coroutines an example of parallel computation? Why or why not?
  3. Explain the difference between the Erlang-style and the Go-style of process communication.
  4. Explain the difference between modeling concurrency with threads and modeling concurrency with an event queue.
  5. What is a race condition?
  6. What are deadlock and starvation?
  7. What do the terms safety and liveness refer to in a concurrent system?

Metaprogramming

  1. What is it about Ruby that make its metaprogramming facilities among the most powerful and expressive of all major programming languages?

Problems

Here are some problems that require some thinking, and some actual work. They may involve writing little scripts, or making sketches. They aren’t exactly short-answer problems. But many are worth doing for the practice.

Syntax and Semantics

  1. Draw the concrete syntax tree for the expression 8 * (13 + 5), assuming a grammar with variables named Expression, Term, Factor, Primary, and intlit.
          Expression
              |
            Term
          /   |   \
      Term    *    Factor
        |       /    |     \
     Factor   (  Expression  )
        |         /    |  \
    Primary Expression +  Term
        |         |         |
     intlit     Term      Factor
                  |         |
               Factor    Primary
                  |         |
               Primary    intlit
                  |
               intlit
    
  2. Draw the abstract syntax tree for the expression 8 * (13 + 5).
        *
      /   \
    8      +
          /  \
        13    5
    
  3. Give an Ohm grammar for expressions in a language in which:
    • The lowest precedence operators are the binary unless, if, while, and until operators, which are non-associative.
    • The next lowest precedence operators are the logical binary operators, which are left associative amongst themselves, but do not associate with each other, meaning that one while can write A and B and C, one may not write A and B or C.
    • The next lowest precedence operators are the relational operators (<, <=, ==, !=, >=, and >), which are non-associative.
    • Next in precedence come the left associative shift operators (<< and >>)
    • Next come the left multiplicative operators (*, /, and %)
    • Next come the left associative additive operators (+ and -)
    • Next come the right associative exponentiation operator (**)
    • Next come non-associative unary operators. The unary operators - and not are prefix, while ! (for factorial) is postfix. Because these are non-associative, one cannot write - - 2 or -5! or 3!!!.
    • The most primitive expressions are identifiers, numeric literals, string literals, ranges (of the form [e1 .. e2] for expressions e1 and e2), function calls, and of course, parenthesized expressions.

    Assume the existence of variables id, numlit, stringlit, and Call.

  4. Give the phrase syntax of a little language with integer-valued variables, assignment statements, while statements, read and write statements, and the usual arithmetic, relational operators, such that type checking between integers and booleans is done in the syntax. Hint: you will need separate sets of variables for arithmetic and logical expressions.
    Program   =  Block
    Block     =  (Stmt ";")+
    Stmt      =  Id "=" Exp
              | read Id ("," Id)*
              | write Exp ("," Exp)*
              | while BoolExp loop Block end
    Exp       =  Exp1 (("+" | "-") Exp1)*
    Exp1      =  Exp2 (("*" | "/") Exp2)*
    Exp2      =  "-"? Exp3
    Exp3      =  intlit | id | "(" Exp ")"
    BoolExp   =  BoolExp1 (or BoolExp1)*
    BoolExp1  =  BoolExp2 (and BoolExp2)*
    BoolExp2  =  Exp (("<" | "<=" | "==" | "!=" | ">=" | ">") Exp)?
              | BoolExp3
    BoolExp3  =  not? BoolExp4
    BoolExp4  =  true | false | "(" BoolExp ")"
    
  5. Give the abstract syntax tree for the following C++ expression
    (a = 3) >= m >= ! & 4 * ~ 6 || y %= 7 ^ 6 & p
    
  6. Give the abstract syntax for the following C fragment:
    int f(int x, int y) {
       return y,x?a.p[5]+=7&!y<x<y+++x||+x|*&y-~--y*x^y:0;
    }
    
  7. Give the abstract syntax for the following C fragment:
    printf("%08X %#16.7e\n",x---a.p[9]^x|*&p&1,*((float*)&x));
    
  8. Give the abstract syntax tree for the following Perl fragment (die and split are both list operators):
    die "\n">>1,~\$$x^split @a,3|x**$;=>%p&&&p;
    
  9. Here is the description of a language. Programs are made up of a non-empty sequence of function declarations, terminated by semicolons, followed by a single expression. Each function declaration starts with the keyword fun followed by the function's name (an identifier), then a parenthesized list of zero or more parameters (also identifiers) separated by commas, then an equals sign, then the body which is a single expression. Expressions can be numeric literals, string literals, identifiers, function calls, or can be made up of other expressions with the usual binary arithmetic operators (plus, minus, times, divide) and a unary prefix negation and a unary postfix factorial ("!"). There is also an infix binary operator called then which indicates that both of its expressions be evaluated in order from left to right, with the value of the right expression being the value of the entire then-expression. There's a conditional expression that looks just like the one in Java, C, and Perl (with the question mark and colon). Factorial has the highest precedence, followed by negation, the multiplicative operators, the additive operators, conditional and finally the then operator. Parentheses are used, as in most other languages, to group subexpressions. Numeric literals are non-empty sequences of decimal digits with an optional fractional part and an optional exponent part. String literals are as in C. Identifiers are those non-empty sequences of letters, decimal digits, underscores, at-signs, and dollar signs, beginning with a letter or dollar sign, that are not also reserved words. Function calls are as in C, with the arguments in a comma-separated list of expressions bracketed by parentheses. There are no comments in this language, and whitespace can be used liberally between tokens.
    1. Write an example program in this language that shows off everything described above.
    2. Give a syntax for this language.
    3. Write a program in this language that consists of the declaration of the GCD function followed by a call to this function with the arguments 99 and 66.
    4. Show the abstract syntax tree for your GCD program.
  10. In Lisp and its dialects, most of the arithmetic operators are defined to take two or more arguments, rather than strictly two. Thus (* 2 3 4 5) evaluates to 120, and (– 16 9 4) evaluates to 3. Show that parentheses are necessary to disambiguate arithmetic expressions in Lisp (in other words, give an example of an expression whose meaning is unclear when parentheses are removed). Why then, does the author Michael L. Scott say in his book that “Issues of precedence and associativity do not arise with prefix or postfix notation?” Reword this claim to make explicit the hidden assumption.
    Scott is assuming all operators have a fixed arity. What he meant to say was: “Issues of precedence and associativity do not arise with prefix or postfix notation assuming all operators have fixed arity.”
  11. TODO: Write a denotational semantics for...
  12. TODO: Write an operational semantics for...

Names, Scopes, and Bindings

  1. Give three examples from C which a variable is live but not in scope. Make sure each example is of a different quality than the others; for example, don't just hide three global variables in a single function and claim you have three examples.
  2. This fragment of Java code illustrates something about scope. Or does it? Relate it to other similar problems we've seen regarding scope. (Don't forget to come across as being articulate and intelligent in your discussion.)
    public void fail() {
        class Failure extends RuntimeException {}
        throw new Failure();
    }
    
  3. Show the output of the following, assuming dynamic scope and (a) deep binding, and (b) shallow binding.
    function g(h) {
      var x = 2;
      h()
    }
    
    function main() {
      var x = 5
      function f() {
        print x + 3
      }
      g(f)
    }
    
    main()
    
    (a) 8, (b) 5
  4. Show the output of the following, assuming dynamic scope and (a) deep binding, and (b) shallow binding.
    function f(a) {
      let x = a - 1
      function g() {
        print x - 17
      }
      h(g)
    }
    function h(p) {
      let x = 13
      p()
    }
    f(18)
    
  5. Explain what would need to be done to make deep binding work with dynamic scoping, assuming that association lists were used to implement the scope rules. (Hint: think of turning the association lists into "A-trees".)
    When you call the passed function, save the existing pointer to the top of the association list and replace it with a pointer to just before the point that the function was defined. Then enter the bindings for the new function as a "branch" in the association list (which is now a tree). When the function finally returns, chop off that branch and restore the pointer.

Types

  1. Is JavaScript 100% weakly typed? About what percentage is it? Why, exactly, is it not?
    No, JavaScript is not 100% weakly typed; The values null and undefined do not get coerced to objects, and non-functions do not get coerced to functions. I would say it's roughly 90% weakly typed. It’s not 100% weakly typed because its designer thought that coercing things to functions and even objects would be going too far, since there is no consensus on the obvious coercion to make.
  2. In the Java programming language, if the class Dog were a subclass of class Animal, then objects of class Dog[] would be compatible with the type Animal[]. Write a fragment of Java code that shows that this means that Java is not completely statically typed. Include in your answer a well-written explanation that shows you truly understand the difference between static and dynamic typing.
    If both Dog and Rat are subclasses of Animal, this code
      Animal[] pets = new Dog[4];
      pets[0] = new Rat();
    
    compiles fine but when executed throws an ArrayStoreException, that's right, a run-time typecheck error. This means Java is NOT 100% statically typed because this typecheck occurs at run time. A language can only be called 100% statically typed if all type conflicts are detected at compile time.
  3. Here's a variation of M-J. Dominus' Spectacular Example.
    local
        fun split [] = ([],[])
          | split [h] = ([h], [])
          | split (x::y::t) = let val (s1,s2) = split t in (x::s1,y::s2) end
        fun merge c ([], x) = x
          | merge c (x, []) = x
          | merge c (h1::t1, h2::t2) =
              if c(h1,h2)<0 then h1::merge c(t1,h2::t2) else h2::merge c(h1::t1,t2);
    in
        fun sort c [] = []
          | sort c x = let val (p, q) = split x
                         in merge c(sort c p, sort c q)
                       end;
       end;
    
    1. During type inference, give the types assigned to
      • the parameter c within sort
      • the function split
      • the function merge
      • the y in the third clause of split?
    2. Give the type of sort and explain why it is not what you would expect.
    3. How do you rewrite the function to make it actually do a mergesort?
  4. Here's some code in some language that looks exactly like C++. It is defining two mutually recursive types, A and B.
    struct A {B* x; int y;};
    struct B {A* x; int y;};
    
    Suppose the rules for this language stated that this language used structural equivalence for types. How would you feel if you were a compiler and had to typecheck an expression in which an A was used as a B? What problem might you run into?
  5. Consider the following C declaration, compiled on a 32-bit little endian machine:
    struct {
        int n;
        char c;
    } A[10][10];
    
    If the address of A[0][0] is 1000 (decimal), what is the address of A[3][7]?
  6. Explain the meaning of the following C declarations:
    double *a[n];
    double (*b)[n];
    double (*c[n])();
    double (*d())[n];
    
  7. Consider the following declaration in C:
    double (*f(double (*)(double, double[]), double)) (double, ...);
    
    Describe rigorously, in English, the type of $f$.

Pointers and References

  1. If possible, write a program in Go that makes a variable point to itself. That is, for some variable x, make it so that *x == x. If this is not possible, state why it is not possible.
  2. If possible, write a program in Rust that makes a variable point to itself. That is, for some variable x, make it so that *x == x. If this is not possible, state why it is not possible.
  3. If possible, give C++ type and object declarations to make a variable point to itself, that is, make it so that p == *p. If it is not possible to do so, state why no such variable can be defined.
  4. In C++ you can say (x += 7) *= z but you can't say this in C. Explain the reason why, using precise, technical terminology. See if this same phenomenon holds for conditional expressions, too. What other languages behave like C++ in this respect?

Control Flow

  1. The following pseudocode shows a midtest loop exit:
    while (true)
        line := readLine();
        if isAllBlanks(line) then exit end;
        consumeLine(line);
    end;
    
    Show how you might accomplish the same task using a while or repeat loop, if midtest loops were not available. (Hint: one alternative duplicates part of the code; another introduces a Boolean flag variable.) How do these alternatives compare to the midtest version?
  2. Assume we wanted to write a function called If in Java or C or JavaScript, such that the call If(c, e1, e2) would return e1 if c evaluated to true, and e2 if it evaluated to false. Show why, in these languages, such a function is absolutely not the same as the conditional expression c?e1:e2. You can show a code fragment that would return different results based on whether the function were called versus the conditional expression were evaluated.
    In Java, C, and JavaScript, all function arguments are evaluated before they are called. The evaluation of (p==null ? null : p.value) is null-safe, whereas the call If(p==null, null, p.value) is not: it throws a NullPointerException if p is null. A more blatant example can be seen in the difference between (true ? 1 : formatMyHardDrive()) and If(true, 1, formatMyHardDrive()).

Subroutines

  1. Some languages do not require the parameters to a subroutine call to be evaluated in any particular order. Is it possible that different evaluation orders can lead to different arguments being passed? If so, give an example to illustrate this point, and if not, prove that no such event could occur.
  2. In C++ it is not permitted to have two functions that differ only in return type overload each other. In Ada it is allowed. What is the reason for this situation? Even though other languages do allow this flexibility in overloading, the compiler needs some sophistication. What exactly is involved? Be very precise in your explanation and illustrate it with code fragments.
  3. Write a function that takes in a function f and a list [a0, a1, ..., an-1] and returns the list [a0, f(a1), f(f(a2)), f(f(f(a3))), ...].

    For example, if you pass as arguments the function that doubles its inputs, and the list [4, 3, 1, 2, 2], then the return value would be [4, 6, 4, 16, 32].

    Hint: Do the f(f(f...)) as a separate function. Also you do not have to make your function tail recursive.

  4. Write a tail-recursive JavaScript function that produces the sum of squares in an array. This is just to give you practice with tail-recursion; I know there are better ways to compute the sum-of-squares.
    function ss(a) {
      function s(i, acc) {
        return i == a.length ? acc : s(i+1, a[i]*a[i]+acc)
      }
      return s(0, 0);
    }
    
  5. Write a tail-recursive Ruby method that produces the sum of squares in an array. This is just to give you practice with tail-recursion; I know there are better ways to compute the sum-of-squares.
    def ss(a)
      s = Proc.new{|i,acc| i==a.length ? acc : s[i+1, a[i]*a[i]+acc]}
      s[0, 0]
    end
    
  6. In as many languages as you can (but include C, Java, JavaScript, and Python for sure), write a pair of functions, f and g, such that every time you call f, you get back 5 less than the result of the previous call to f or g, and every time you call g, you get back double the absolute value of the result of the last call to f or g. The initial value is 0. It is possible to do this in one line of Perl.
  7. Write the following function in Standard ML, where your implementation must be tail recursive.

    Given: a list [a0, a1, ..., an-1],
    Return: a0*a1 + a2*a3 + a4*a5 + ....

    For example, if given [3, 5, 0, 28, 4, 7] we return 15 + 0 + 28 = 43. If there are an odd number of elements in the list, assume there is an extra 1 for padding.

    Here is, by the way, a non-tail-recursive formulation:

    fun sum_of_prods [] = 0
      | sum_of_prods (x::nil) = x
      | sum_of_prods (x::y::t) = x * y + sum_of_prods t;
    
  8. Write a JavaScript function, without using eval, that accepts an array of integers a, and a function f, and returns an array of functions, each of which, when called, invokes f on the corresponding element of a.

    For example, if your function was called g, then calling g([6,3,1,8,7,9], dog) would return an array of functions p such that, for example, calling p[3]() would invoke dog(8).

    function g(a, f) {
        var b = [];
        for (var i = 0; i < a.length; i++) {
            b[i] = function(i){return function(){f(a[i])};}(i);
        }
        return b;
    }
    
  9. Write some JavaScript that adds a new method to arrays so that if I call this method on an array with two parameters f and g, I get back a new function which, when called with one argument k, returns the composition of f and g applied to the kth element of the original array. Hint: If we defined the functions square and addSix the obvious way, and we called this new method weird, then:
    [4, 6, 7, 3, 5, 2, 4].weird(addSix, square)
    
    would return the function z such that
    z(2) == 55
    
    because the element at index 2 within the array is 7 and $7^2 + 6 = 55$.
  10. In Go, Rust, Swift, C, and C++ arrays and records can be allocated on the stack, not just on the heap. When making assignments of aggregates to variables, compilers usually generate code to deposit the values in temporary storage. Why is this necessary in general? After all, in
    Weekdays := Day_Set(False, True, True, True, True, True, False);
    

    we could construct the aggregate directly in the variable Weekdays. Give an example of an assignment statement that illustrates the necessity of constructing an aggregate in temporary storage (before copying to the target variable).

  11. In Java, you generally implement callbacks via registration of listeners that implement a known interface, rather than using method pointers. Create a Swing component called AngleReader which displays a picture of a circle and notifies all its listeners of the angle, in degrees, that the mouse cursor makes with the horizontal axis of the circle as the mouse moves over it.
  12. Complete the following definition of a dot product function in ML:
    val dot =
        let
            fun transpose ... =
        in
            ....
        end;
    
    The transpose function should work like this
    transpose ([x1,...,xn],[y1,..,yn]) = [(x1,y1),...,(xn,yn)]
    
    raising Domain if the arrays have different lengths. The body of the definition of dot (between the in and end) should contain only instances of the functions transpose, o, foldr, map, op*, op+, and the value 0.
  13. Explain what is printed under (a) call by value, (b) call by value-result, (c) call by reference, (d) call by name.
    x = 1;
    y = [2, 3, 4];
    function f(a, b) {b++; a = x + 1;}
    f(y[x], x);
    print x, y[0], y[1], y[2];
    
    1. Under call by value, the arguments do not change; so the script prints 1 2 3 4.
    2. Under call by value/result, the increment of x does not take place until after the subroutine returns. It prints 2 2 2 4.
    3. Under call by reference, the increment of b changes x immediately, so the new value of x, namely 2, is used to update a, which is still y[1], and that becomes 2 + 1 = 3, so it prints 2 2 3 4.
    4. Under call by name, b++ changes x immediately so x becomes 2. Then, since a refers to the expression {y[x]}, it will need to compute y[2] which is 3. The script prints 2 2 3 3.
  14. Explain what is printed under (a) call by value, (b) call by value-result, (c) call by reference, (d) call by name.
    x = 1;
    y = 2;
    function f(a, b) {a = 3; print b, x;}
    f(x, x + y);
    print x;
    
  15. Using your favorite language and compiler, write a program that determines the order in which subroutine arguments are evaluated.
  16. Consider the following (erroneous) program in C:
    void f() {
        int i;
        printf("%d ", i++);
    }
    int main() {
        int j;
        for (j = 1; j <= 10; j++) f();
    }
    
    Local variable i in subroutine f is never initialized. On many systems, however, the program will display repeatable behavior, printing 0 1 2 3 4 5 6 7 8 9. Suggest an explanation. Also explain why the behavior on other systems might be different, or nondeterministic.
  17. Give an example which shows that default parameters are unnecessary in C++ because you can always get the desired effect with overloading.
  18. What does the following program output?
    with Ada.Text_IO, Ada.Integer_Text_IO;
    use Ada.Text_IO, Ada.Integer_Text_IO;
    
    procedure P is
      A: Integer := 4;
      type T is access Integer;
      B: T := new Integer'(4);
      C: T := new Integer'(4);
    
      procedure Q (X: in out Integer; Y: T; Z: in out T) is
      begin
        X := 5;
        Y.all := 5;
        Z.all := 5;
      end Q;
    
    begin
      Q (A, B, C);
      Put (A);
      Put (B.all);
      Put (C.all);
    end P;
    
  19. In some implementations of an old language called Fortran IV, the following code would print a 3. Can you suggest an explanation? (Hint: Fortran passes by reference.) More recent versions of the Fortran language don't have this problem. How can it be that two versions of the same language can give different results even though parameters are officially passed "the same way." Note that knowledge of Fortran is not required for this problem.
          call f(2)
          print* 2
          stop
          end
          subroutine f(x)
              x = x + 1
              return
          end
    

Modules, Classes, and Abstract Data Types

  1. Make a Perl module with a function called nextOdd. The first time you call this subroutine you get the value 1. The next time, you get a 3, then 5, then 7, and so on. Show a snippet of code that uses this subroutine from outside the module. Is it possible to make this module hack-proof? In other words, once you compile this module, can you be sure that malicious code can't do something to disrupt the sequence of values resulting from successive calls to this function?
  2. Consider the implementation of a Container class framework with the following abstract base class Container:
    template <class Item>
    class Container {
    public:
      unsigned numberOfItems() {return currentSize;}
      bool isEmpty() {return currentSize == 0;};
      virtual void flush() = 0;
      ~Container() {flush();}
    private:
      unsigned currentSize;
    };
    

    Here the idea is that each particular (derived) container class shall implement its own flush() operation (which makes sense because different containers are flushed in different ways: there may be arrays, linked lists, rings or hashtables used in the representation), and when a container is destroyed its flush() operation will be automatically invoked. However, the idea is flawed and the code as written causes a terrible thing to happen. What happens?

Object-Orientation

  1. What philosophers call a "class" mathematicians call a "set" (i.e., a collection of unordered, unique, values). So, a philosopher says that every member of a subclass is also a member of the superclass. But a C++ programmer says that every member of a superclass is also a member of its subclass! What is going on here?
  2. Write a three-page paper on the nature of identity in object oriented philosophy. Include some code fragments to illustrate your main points.
  3. It is certainly possible to make a Person class, then subclasses of Person for different jobs, like Manager, Employee, Student, Monitor, Advisor, Teacher, Officer and so on. But this is a bad idea, even though the IS-A test passes. Why is this a bad idea and how should this society of classes be built?
  4. One of the most important ways in which object oriented programming helps us to manage complexity is through the ability to group related classes into a hierarchy of subclasses and superclasses. Dynamic binding (run-time polymorphism) allows us to operate on collections of objects from different classes in a hierarchy safely. Furthermore, systems which are programmed using dynamic binding are more easily extendible.
    1. What construct is used in non-object oriented programming languages to simulate class hierarchies, and why do we say that it is unsafe?
    2. Why are inheritance-like structures in non-OOPLs harder to extend?
  5. Why is it said that implementation inheritance is at odds with encapsulation?
  6. Inheritance is not always appropriate. Discuss the reasons why a design with a superclass Person and subclasses for different jobs (e.g., programmer, manager, ticket agent, flight attendant, supervisor, student, etc.) is a lousy design. Give an alternative.
  7. In designing a class hierarchy in C++, when should you make an operation virtual and when should you make an operation non-virtual? Give examples.
  8. Write a Perl "class" for machine parts that have an identification number (a positive integer divisible by 5), a weight (a positive floating-point number) and a name (which must consist entirely and exclusively of alphabetic characters). Provide a "constructor" that takes in a string consisting of the id, weight, and name, respectively in which
    • the string may have leading and trailing spaces
    • the three fields are separated by a vertical bar
    • the weight is not expressed in scientific notation: it can have an integral value, but if it does have a decimal point, then it is followed by a non-empty fractional part. There is no "E" part, ever. It's simple.
    The constructor will check for a valid argument by matching against a regex, and if all is cool, will split the string to assign to its fields.
  9. Find some old "procedural" code you have written and rewrite in an object-oriented fashion. I don't mean that you have to use inheritance or polymorphism; all I am really looking for is that you wrap some functions up in a sensible class.
  10. A common pattern that comes up a lot is the need to assign unique identifiers to objects of a given class, for example:
    class Item {
        private int id;
        private static int numberOfItemsCreated = 0;
        public Item() {id = numberOfItemsCreated++;}
        // pretend that there are more members here...
    };
    
    As you can see, every item that gets created will get a unique id. Because this pattern occurs frequently, it might be nice to generalize this and make make something reusable out of it so we don't have to write this code inside every class that needs ids. Perhaps we need an interface or abstract class. Tell me why these two suggestions won't work with a detailed, technical answer. Then tell me something that will work. (Note: there is nothing wrong with the access modifiers above; the problems with my two suggestions have to do with the nature of interfaces and abstract classes.)
  11. Given a utility class, can you always rewrite it as a singleton? Given a singleton, can you always rewrite it as a utility class? If so, when would you choose one over the other?
  12. Explain how, in C++, you can get access to, and indeed modify, a protected component of an object that someone else declared. As a concrete example, let's say someone has declared
        class C {protected: int x; ...};
        C c;
    

    then your job is to assign a new value to c.x. Assume there are no public operations of C that modify x that you know of. Also, do not use any preprocessor tricks (like #define protected public).

  13. What makes more sense, to inherit a list from a stack or a stack from a list?
  14. Why did the designers of the C++ standard library containers emphatically reject an inheritance hierarchy of containers?
  15. In C++ you can write
    class C {int x;};
    C c;
    C* p = &c;
    cout << p;
    
    and there is no compile-time nor link time error, despite the fact that operator<<(C*) is not a member of ostream (since that class was declared before you declared C), nor for that matter did anyone declare the global function
        ostream& operator<<(ostream&, C*);
    
    So why does it all work? Explain exactly what gets printed and why.
  16. What happens to the implementation of a class if we "redefine" a field in a subclass? For example, suppose we have:
    class Foo {
        public int a;
        public String b;
    }
    ...
    class Bar extends Foo {
        public float c;
        public int b;
    }
    
    Does the representation of a Bar object contain one b field or two? If two, are both accessible, or only one? Under what circumstances? Answer for C++, Java, Python, and Scala.
  17. If Foo is an abstract class in a C++ program, why is it acceptable to declare variables of type Foo*, but not of type Foo? Does this problem even make sense to ask in Java or Python?

Concurrency

  1. Implement a priority queue data type in Ada, using a server task to provide synchronization.
  2. Implement a priority queue data type in Ada, where each priority queue object is a protected object.
  3. In the example Ada package implementing the Set data type with a guardian task, there is a serious problem with the package design: errors in insertion are not handled well! What happens if we run out of memory? (Answer in terms of the system interfaces.) Show how to add robust error handling to the package and comment on the amount of parallelism permitted with your solution.
  4. Discuss the difficulties of implementing a secure Post Office object in Ada that meets the following requirements. The post office is to maintain a collection of P.O. boxes, each belonging to some task. Any task can put a letter into another task's box, but only the owner of a particular box can open it and read the letters.
  5. A relay is an agent task created by one task to relay a message to another. For example, if a calling task wishes to send a message to another but does not wish to wait for a rendezvous, the caller can create a relay task to send the message. (Note that relays are only appropriate to use when there are no out parameters in the called entry.) Sketch in detail a body for an Ada task T that uses a relay R to call entry E of task U, passing message X.
  6. In Ada, if two tasks are executing a Put procedure at the same time, their outputs may be interleaved. (This could produce amusing and even distasteful results, e.g. writing "sole" in parallel with "ash") Show how to set things up so only one task is writing at a time.
  7. Why do Java programmers not have to worry about the situation in the previous problem (interleaving of text output written to a stream)?
  8. One of the nice features of Quicksort is that it allows a great deal of parallelism in an implementation. After partitioning, the slices on either side of the pivot can be sorted in parallel. It is very easy to set things up to do this in languages such as occam, but tedious in Ada and Java. Code up a parallel version of Quicksort in Ada or Java and explain why it is messy.
  9. In Ada, what happens when you try to call an entry in a task that has terminated? Comment on the following code fragment as a possible approach to calling entry E of task T only if T has not terminated.
    if not T'Terminated then
        T.E;
    end if;
    
  10. In Java, what happens if you invoke a method on a thread that has completed?
  11. Here's an open-ended question many students hate, especially on exams. Two JavaScript programmers are arguing over the best way to implement a little timer widget. The first programmer prefers:
    let countDown = function () {
        let i = 10;
        let update = function () {
            document.getElementById("t").innerHTML = i;
            if (i-- > 0) setTimeout(update, 1000);
        }
        update();
    }
    
    The second argues that this is better:
    let countDown = function () {
        let update = function (i) {
            document.getElementById("t").innerHTML = i;
            if (i-- > 0) setTimeout(function () {update(i)}, 1000);
        }
        update(10);
    }
    
    Your role is to figure out which programmer is right, if any. Make a fairly extensive list of the pros and cons of each approach. Your list will be graded on completeness, neatness, correct usage of terminology (don't forget to mention "anonymous function" and "closure"), and how articulately you express yourself. Bad grammar will impact your grade negatively. You may want to consider readability and (especially) performance.