Study questions

Course links

May 7, 2008

  1. Use Krishnamurthi's automaton macro to define a predicate that tests whether a given list of symbols (a) consists entirely of the symbols foo and bar and (b) contains a sublist of three adjacent symbols in which the first is foo and the last is bar.
  2. As you may have discovered in working the previous exercise, an application of Krishnamurthi's automaton macro actually expands to a procedure that returns true when given any prefix of a list that the automaton should accept. For instance, in his very first example, his macro produces an automaton that accepts '(0 1 0), which is not of the form (01)*. Extend the macro to provide a mechanism for the programmer to specify which states of the automaton are "accepting" states. The predicate derived from the macro application should still return true if the automaton is in an accepting state when it reaches the end of the list, but should return false if it is in a non-accepting state at that point.

May 5, 2008

(in preparation)

May 2, 2008

  1. In some respects, Prolog's mechanism for dealing with the success or failure of a unification attempt seems quite similar to Icon's goal-directed evaluation, which we studied back on March 7. To implement Icon-like generators (in GCFAE, we used continuation-passing style, supplying, in each call to our interp function, a success continuation to be used if the expression could produce one more value and a failure continutation to be used if it could not. Compare and contrast this approach with the one that Krishnamurthi proposes for Prolog, in which "every Prolog expression ... consumes a continuation that dictates where to resume in case of failure, and ... returns its continuation to indicate success."
  2. Explain how Prolog's !-expression ("cut") might be implemented in terms of continuations.

April 30, 2008

  1. Add clauses to Krishnamurthi's Prolog definition of the type relation to accommodate nil-, ifnil-, newpair-, opencar-, and opencdr-expressions, imposing the the restrictions that the cdr of a pair must be a list and that all of the elements of a list must be of the same type.
  2. Since Prolog generally does not perform the "occurs check," are there any conditions under which Prolog-based type inference using the extended relation you obtained in the previous exercise will fail to terminate? If so, give an example; if not, show informally that no such failure is possible.

April 25, 2008

  1. In Prolog, define a ternary relation that is satisfied by X, Y, and Z if, and only if, Z is a common ancestor of X and Y in the "academic family tree" that Krishnamurthi describes in section 33.1. Then define a binary relation that holds between X and Y if they have a common ancestor.
  2. Add clauses to Krishnamurthi's Prolog definition of the type relation to accommodate +-expressions and with-expressions, as in FWAE. Does Prolog automatically support let-based polymorphism?

April 23, 2008

  1. Why are type closures needed for type inference in languages that support "let-based" polymorphism? What data must a type closure contain in order to make this work?
  2. Why should languages that support let-based polymorphism prohibit the use of side effects in connection with identifiers that have polymorphic types?

April 21, 2008

  1. List the constraints on the types of subexpressions in Krishnamurthi's definition of map on page 282. Then execute the unification algorithm to infer the types of those subexpressions, confirming or refuting Krishnamurthi's identification of the inferred type of map.
  2. In implementing Hindley-Milner type inference for a language in the AE-series that supports first-class functions, would it be appropriate to perform the "occurs check" that Krishnamurthi discusses in section 30.5.4? Could there be a case in which the occurs check is necessary to prevent an infinite loop in the type-inference procedure? Justify your answers.

April 18, 2008

  1. In TFAE, the concrete syntax for our type system is given by the BNF productions
    <type> ::= "number"
             | "(" <type> "->" <type> ")"
    
    and the corresponding abstract syntax is represented by the datatype definition
    (define-type TFAE-Type
      (number-type)
      (function-type (parameter-type TFAE-Type?) (return-type TFAE-Type?)))
    
    How might we modify these syntax rules to add list as a type constructor? (Note: We wouldn't have to use the concrete syntax that Krishnamurthi uses in chapter 29; we're building onto TFAE, whereas he's building onto Scheme, so whatever we come up with won't look exactly like his syntax anyway.)
  2. How might we further modify the concrete and abstract syntax rules to add type variables, type abstractions, and type applications?
  3. Krishnamurthi observes that Java 1.5 supports explicit polymorphism. What constructions in Java 1.5 implement type abstraction? What constructions implement type application?

April 16, 2008

  1. Describe how C programmers must interpret the values returned by the library functions malloc, fopen, and scanf in order to detect failures in their execution.
  2. Give an example of an expression in C that, when executed, yields a value of a type other than the one that would be predicted by a static type checker (or specified by the programmer).

April 14, 2008

  1. Extend the type system of TFAE to include "product types": For any types t0 and t1, the type t0*t1 will be the type of ordered pairs in which the car is of type t0 and the cdr of type t1. Extend the syntax and semantics of TFAE to include expressions and values of product types.
  2. Write a BNF rule for datatype-expressions, as Krishnamurthi appears to be using them in chapter 27. You may need to adjust the BNF for the <type> syntax category as well.

April 11, 2008

  1. Answer the question Krishnamurthi poses in Exercise 26.3.1: "We've been told that the Halting Problem is undecidable. Yet here we have a language accompanied by a theorem that proves that all programs will terminate. In particular, then, the Halting Problem is not only very decidable, it's actually quite simple: In response to the question 'Does this program halt', the answer is always 'Yes!' Reconcile."
  2. Is the type annotation that appears in rec-expressions redundant, like the return-type annotation in fun-expressions? What reason might there be to require it?
  3. Add boolean literals, conditionals, and rec-expressions to our interpreter for the TFAE language to get an interpreter for TRCFAE. Be sure to modify the parse, interp, and type-check procedures to handle them correctly.

April 9, 2008

  1. Define a PLAI Scheme data type TFWAE-Type of which each individual value represents a data type of TFWAE. Then write a procedure type-check that takes an abstract syntax object of TFWAE and returns its type, if it satisfies the typing rules Krishnamurthi proposes, or #f if it does not.
  2. An alternative design for type-check would be to add one more value to the TFWAE-Type data type: TypeError, which would represent the pseudo-type of a syntactically correct expression of TFWAE that does not satisfy the typing rules. Then type-check could always return a TFWAE-Type. Implement this design and compare its strengths and weaknesses with those of the type-check procedure you wrote in the preceding exercise.

April 7, 2008

  1. Construct the type judgement tree for the expression
    {fun {x : number} : (number -> number)
      {fun {y : number} : number
        {+ x y}}}
    
    of TFWAE.
  2. Propose a typing rule for with-expressions in TFWAE. Justify your rule by deriving it from the typing rules for fun-expressions and applications, together with the syntactic rule for preprocessing with-expressions.
  3. Explain in your own words why every type system either blocks some programs that can execute without error or fails to block some programs that cannot execute without error.

April 4, 2008

  1. Give a semantic rule for with-expressions and show how it is related to the rules for fun-expressions and applications.
  2. How would one adapt the semantic rules for fun-expressions and applications to accommodate functions of all (fixed) arities?
  3. Using the semantic rules from chapter 23 of our text, prove that the value of the expression {{fun {augend} {+ augend 3}} {+ base 5}}, in an environment that maps base to 9, is 17.

April 2, 2008

  1. The concrete syntax that is generally used for combinatory logic is different from the one we've been using. To begin with, parentheses are used instead of braces. Function application is assumed to group from left to right, and the parentheses around an application are omitted whenever this convention would make them superfluous, as well as around the outermost application. Finally, since the combinators are generally single letters (sometimes with subscripts or superscripts), the spaces between them are also omitted. Thus, for example, the expression {{C {{J I} C}} {J I}} would more usually be written as C(JIC)(JI). Write a Scheme procedure that takes an object of the CL datatype and outputs it in this more concise concrete syntax.
  2. Count the number of reductions that occur during the evaluation of the program
    {with {fact {lam f {lam n {if {iszero n}
                                  one
                                  {{prod n} {f {pred n}}}}}}}
          {with {factorial {make-recursive-procedure fact}}
                {factorial {{sum three} one}}}}
    
    first in the simpler implementation of combinatory logic (without the B and C combinators), then in the implementation with those combinators. Is the difference significant? Could adding more primitive combinators improve performance?

March 31, 2008

  1. Show that the I combinator in our implementation of combinatory logic is theoretically superfluous by defining an equivalent function, using only the K and S combinators.
  2. Revise the implementation of combinatory logic so that the interpreter uses eager reduction rather than leftmost reduction. (In eager reduction, the function and argument subexpressions in any application are fully evaluated -- that is, reduced until no further reductions are possible -- before the application itself is reduced.)
  3. Describe the combinator versions of the Church numerals.

March 14, 2008

  1. Explain why the evaluation of the expression
    {with {fact {lam f {lam n {if {iszero n}
                                  one
                                  {{prod n} {f {pred n}}}}}}}
          {with {factorial {make-recursive-procedure fact}}
                {factorial {{sum three} one}}}}
    
    in our eager interpreter for the untyped lambda-calculus leads to runaway recursion. How could we modify the interpreter to keep that from happening?
  2. The Church numerals that Krishnamurthi introduces in chapter 22 denote only natural numbers. Suppose that we want to perform computations involving signed integers. Is it possible to model the signed integers in the untyped lambda-calculus? If so, suggest an implementation; if not, explain why not.

March 12, 2008

  1. In section 22.1, "Encoding lists," Krishnamurthi describes an implementation of ordered pairs, which can play the same role in our minimalist programming language that cons cells play in Scheme. But he neglects to provide an implementation of the null list! Any function could play this role, provided that there is a way to write an isnull function that distinguishes our chosen null from any pair that the pair-constructor can possibly return. Suggest an implementation.
  2. Define a function that takes two Church numerals and returns yes if the first denotes a number greater than the number that the second denotes, and no otherwise. (Hint: Use recursion.)
  3. Do exercise 22.4.1 from the text (page 229).

March 10, 2008

  1. A storage location is "reachable from the root set" if it is bound to a global variable or to a parameter or local variable in a function that has been invoked but has not yet returned or if its address is stored in some value that is itself reachable from the root set. In some procedural languages, it is safe to deallocate any storage location that is not reachable from the root set. In C, however, this is not generally safe. Why not?
  2. Many garbage collection algorithms divide unallocated memory into different pools, for allocating storage blocks of different sizes. Why might this be a useful optimization?
  3. "Generational" garbage collection algorithms divide memory into several pools and gradually migrate storage blocks from one to another as they survive more and more mark-and-sweep steps. Why might this be a useful optimization?

March 7, 2008

  1. The file /home/stone/courses/languages/examples/GCFAE.ss contains an interpreter for an AE-series language that has alt and range operators analogous to the | and to ... by operators in Icon. The file ends with seven commented-out test cases. Write down what you expect the results of running those test cases would be, then uncomment and run them, then compare the actual results to your expected results and write up an explanation of any differences that you find.
  2. Icon has a keyword expression &fail that, when evaluated, fails. Add a similar expression fail to GCFAE.
  3. Add a not-expression to GCFAE. It should take one operand, failing if evaluation of that operand succeeds and succeeding (once, with the value 0) if evaluation of the operand fails.

March 5, 2008

  1. Following Krishnamurthi's suggestion on page 197, add a throw-expression to the KCFAE language, and remove the possibility of using a continuation-valued expression in the operator position of an application.
  2. In the interpreter you constructed in the preceding exercise, the last two examples Krishnamurthi gives on page 203 are no longer semantically valid and will cause errors in the interpreter. Why?
  3. An until-expression evaluates its body once immediately, then tests to see whether its exit condition is true, terminating if so and repeating if not. The value of an until-expression is the result of the last evaluation of the body. Give two definitions of until, one as a Scheme procedure that takes its body and exit-condition as (procedure-valued) arguments, analogous to Krishnamurthi's definition of for on page 201, and the other as new Scheme syntax (introduced through define-syntax).

March 3, 2008

  1. Define web-read in terms of let/cc and web-read/k.
  2. Write a producer body that applies its argument (send) to each of the non-negative powers of 2, in ascending order. This producer body will probably contain an infinite loop. Why doesn't this matter?
  3. Implement the Sieve of Eratosthenes in Scheme, using generators. (Hint: Study the Haskell version in /home/stone/courses/languages/examples/Eratosthenes.hs, then define mark, sieve, intsFrom, and primes as Scheme procedures. The Haskell type declarations are a useful guide -- if you read "[Integer]" as "generator of integers"!)

February 29, 2008

  1. Use the Fischer CPS transformation to convert the expression
    ((lambda (x)
       (+ (web-read "Augend: ") x))
     42)
    
    to continuation-passing style.
  2. A human being would transform the expression shown in the previous question into
    (lambda (final-k)
      (web-read/k "Augend: " (lambda (l-val)
                               ((lambda (x dyn-k)
                                  (dyn-k (+ l-val x)))
                                42
                                final-k))))
    
    or perhaps even into
    (lambda (final-k)
      (web-read/k "Augend: " (lambda (l-val)
                                (final-k (+ l-val 42)))))
    
    What simple optimization could convert the output of the Fischer transformation into this last version?

February 27, 2008

  1. Do exercise 17.1.1 from the textbook.
  2. Read about contracts in chapter 14 of PLT MzLib: Libraries manual, then do exercises 17.6.1 and 17.6.2 from the textbook. Assume that the contract for web-read is (-> string? number?) and the contract for web-display is (-> number? any).

February 25, 2008

  1. How does HTML FORM element transmit a user-supplied string to a program that it invokes? How does it transmit a "hidden" string?
  2. Use your favorite Web-scripting language to write an addition server, following the plan elaborated in section 16.4 of the textbook. What difficulties might you encounter in generating the three component programs automatically from the Scheme version at the beginning of the section?
  3. The Web server that Krishnamurthi describes in section 16.2, which maintains an internal hash table of receivers, would not have to use the hidden-field mechanism of HTML forms. Explain why not.

February 22, 2008

  1. In the Java statement arr[0] := 42;, where arr has been declared to be an array of seventeen ints, does the expression arr[0] have an l-value? Justify your answer. Similarly, consider the C fragment p = &arr[0]; *p = 42;, where arr is an array of seventeen ints and p is of type [int *]. Does the expression *p have an l-value?
  2. Krishnamurthi's RVCFAE language requires the operand in any application of a call-by-reference function to be an identifier. Some real-world languages -- Pascal, for instance -- impose similar restrictions. Other languages that use call-by-reference, such as FORTRAN, permit any expression in the operand position of a call-by-reference application, but allocate and initialize a new storage location automatically for any expression that has no l-value. Adapt the RVCFAE interpreter so that it has this feature.

February 20, 2008

  1. Write a BCFAE program that creates two boxes (each containing a different odd number), calls a swap function to exchange their contents, and finally computes the sum of the the contents of the first box and double the contents of the second box.
  2. In chapter 13, Krishnamurthi's BCFAE language introduces boxes as values for expressions and binds function parameters to newly allocated storage locations rather than directly to the values of the corresponding arguments. It is possible, however, to introduce boxes without changing the way the function parameters work. Write an interpreter for a language with the same syntax as BCFAE, but with no storage allocation for parameters (the values of parameters are saved in environments, not in the store). Is it still necessary to be careful about the sequencing of side effects in this BCFAE variant? Are there any expressions that have different values in BCFAE and this BCFAE variant? If so, give an example.
  3. Do exercise 13.4.3 on page 130 of the textbook.

February 18, 2008

  1. In testing the interpreters from chapter 11 in which Scheme procedures are used to represent environments, one encounters some cases in which the tests fail unexpectedly:
    > (test (interp (parse '{with {x 3}
                              {fun {y}
                                {+ x y}}})
                    (mtSub))
            (closureV 'y (add (id 'x) (id 'y)) (aSub 'x (numV 3) (mtSub))))
    
    (list 'bad
          (closureV 'y (add (id 'x) (id 'y)) (lambda (a1) ...))
          (closureV 'y (add (id 'x) (id 'y)) (lambda (a1) ...))
          "at line 165")
    
    What's the explanation?
  2. Could one write a metacircular interpreter for C? What would be some of the difficulties?
  3. What features would one need to add to RCFAE in order to be able to write a metacircular interpreter for the resulting language?

February 15, 2008

  1. What are the fixed points of the function that takes every string into its reverse?
  2. What is the relationship between the "pre-transformer" ρ' on page 93 and the environment transformer Fe0?
  3. Do exercise 10.0.4 on page 100 of our textbook.

February 13, 2008

  1. Add a Boolean data type to CFAE/L, with expressions having the following concrete syntaxes (in addition to those already in the language):
    <CFAE/L> ::= "true"
               | "false"
               | "{" "<=" <CFAE/L> <CFAE/L> "}"
               | "{" "if" <CFAE/L> <CFAE/L> <CFAE/L> "}"
               | "{" "&&" <CFAE/L> <CFAE/L> "}"
               | "{" "||" <CFAE/L> <CFAE/L> "}"
    
    The literals true and false should simply evaluate to the Boolean values they name, regardless of context. The less-than-or-equal-to expression should evaluate to the true Boolean value if both of its CFAE/L subexpressions evaluate to numbers and the one on the left is less than or equal to the one on the right; it should evaluate to the false Boolean value if both of its CFAE/L subexpressions evaluate to numbers and the one on the right is less. The test in the if-expression should always be Boolean; the if-expression should have the same value as the consequent if the test is true and the same value as the alternate if the test is false. Both of the CFAE/L subexpressions of an &&-expression should be Boolean, and the whole expression should be true if both subexpressions are true and false otherwise. Similarly, an ||-expression should be true if either of its CFAE/L subexpressions is true, false if neither subexpression is true.
  2. Does your lazy implementation of the extensions mentioned in the preceding exercise ensure that &&-expressions and ||-expressions use "short-circuit" evaluation? If not, modify the implementation to guarantee this property.

February 11, 2008

  1. Under what circumstances, if any, will the "answer" that the FAE/L interpreter returns for a complete program be an expression closure? If this isn't satisfactory, how could we arrange to exclude that possibility?
  2. Add a conditional expression to FAE/L, with the concrete syntax
    <FAE/L> ::= "{" "if0" <FAE/L> <FAE/L> <FAE/L> "}"
    
    and the semantics that the value of the entire expression is the value of the second <FAE/L> when the value of the first <FAE/L> is zero and the value of the third <FAE/L> when the value of the first <FAE/L> is non-zero. What should happen if the value of the first <FAE/L> is a function closure rather than a number?
  3. Is it possible to define a function that invokes itself recursively in the extended language you defined in the previous exercise?

February 8, 2008

  1. In Haskell, define a function powers that takes a positive integer as argument and returns an infinite list containing the powers of that integer, in ascending order, starting from 1. (For instance, the value of take 5 (powers 7) should be the list [1, 7, 49, 343, 2401].)
  2. If you try to compute the length of an infinite list in Haskell, will the system hang (in an infinite loop), or will the fact that Haskell uses lazy evaluation enable it return a sum with an unevaluated addend (something like 1 + (length ones))?
  3. When we implement FAE in the Haskell programming language, the applications of functions like interp and faeLookup are lazy -- the arguments are not evaluated unless and until their values are actually needed in a computation. From the point of view of the FAE user, does this make application in FAE lazy as well? How can you tell?

February 6, 2008

  1. In section 6.3, Krishnamurthi observes that a with-expression is equivalent to an application in which the operand is a fun-expression, so that it's unnecessary to have a with-variant in the data type that expresses FAE's abstract syntax. Is it also possible to reverse this process and replace every fun-expression with an equivalent with-expression, perhaps in combination with other constructs of FAE? If so, show how a preprocessor might do the conversion; if not, explain the nature of the difficulty.
  2. What is a closure? Why are closures needed in the semantics of FAE?
  3. The standard C library provides a qsort function. Describe its interface and explain how it is shaped by the absence of first-class functions from C.

February 4, 2008

  1. When Krishnamurthi introduced a lookup table for function names in chapter 4, why didn't the issue of static vs. dynamic scope arise for them?
  2. For Perl programmers: Why does Perl provide both my and local as ways of declaring variables that are local to a function?
  3. For Mathematica users: Write a sequence of Mathematica statements that demonstrate that Mathematica uses dynamic scope.
  4. Implement deferred substitution in F1WAE using hash tables instead of DefrdSub structures, as Krishnamurthi proposes in Exercise 5.3.3.

February 1, 2008

  1. How many variants are there in the FunDef data type? How is the appropriate number of variants determined?
  2. Suppose that you wanted to replace the two-namespace semantics of F1WAE with a single namespace, in which the local bindings introduced by with would supersede any function definitions. Could this be done in the substitution framework that we're currently using? If so, how? If not, what's the obstacle?
  3. One of the test cases that I ran for this chapter was based on the example that Krishnamurthi gives at the beginning of section 4.3:
    (test (interp (parse '{f 5})
                  (list (parse-fundef '{function f {n} {g {+ n 5}}})
                        (parse-fundef '{function g {m} {- m 1}})))
          9)
    
    This test failed, because interp raised the "function not found" error. Why?
  4. Do exercise 4.3.1 on page 30 of our textbook.

January 30, 2008

  1. Can identifiers in WAE contain underscores? How is this determined in the implementation?
  2. Consider the following expression in Scheme:
    (let loop ((index count))
      (if (zero? index)
          '()
          (cons repetend (loop (- index 1)))))
    
    Are there any binding instances of identifiers in this expression? If so, where do they occur?
  3. In the definition of the calc procedure for WAE, the clause that is supposed to compute the value of an identifier simply invokes error, which interrupts the computation. Why? Shouldn't calc look back to the point at which the identifier was bound and recover the value of the identifier from the value of the named-expr that occurred there?
  4. What changes would we need to make in the parse, subst, and calc procedures for WAE if we wanted it to use lazy evaluation?

January 28, 2008

  1. In the Pascal programming language, a begin-statement consists of the keyword begin, zero or more statements, and the keyword end. Any two otherwise adjacent statements must be separated by a semicolon, and a semicolon may optionally be placed between the last statement, if there is one, and the end keyword. Taking the <statement> category as given, write a description of the syntax of begin-statements in Pascal in extended Backus-Naur form.
  2. The Backus-Naur notation itself has a grammar, which can also be expressed in BNF, taking <syntactic category> and <literal token> as primitives. Write a BNF for (our version of) Extended BNF.
  3. Section 8 of the Lua 5.1 reference manual gives the context-free syntax of the Lua programming languages in "extended BNF." How do the Lua authors' conventions for writing grammars in Backus-Naur form differ from the ones I explained in class? How would you adapt the BNF for BNF that you wrote in the preceding question to fit the Lua authors' conventions?

January 25, 2008

  1. On page 9 of our textbook, Krishnamurthi writes, "Writing a reader is relatively simple: when you see a[n] opening bracket, read recursively until you hit a closing bracket, and return everything you saw as a list. That's it." What parts of the process does this description leave out? Suppose you were asked to implement Scheme's read procedure, using read-char and peek-char as the only available operations on input ports. How would you go about it? At what points, if any, would you use recursive calls to read? What problems would you encounter?
  2. Would there be any advantage in publishing and exchanging Java class definitions as XML files rather as simple .java files?
    <?xml version="1.0" encoding="ISO-8859-1"?>
    <java:class access="public" name="HelloWorld">
      <java:method access="public" per="static" return="void" name="main">
        <java:parameter name="args">
          <java:arraytype>
            <java:type name="String" />
          </java:arraytype>
        </java:parameter>
        <java:block>
          <java:statement>
            <java:expression>
              ...
            </java:expression>
          </java:statement>
        </java:block>
      </java:method>
    </java:class>
    
  3. In class, I (should have) mentioned that the character sequence ## is ill-formed in Scheme. Give another example of a character sequence that is likely to cause an error in scanning.

January 23, 2008

  1. Section 7.2 of the Revised5 report on the algorithmic language Scheme includes an incomplete denotational semantics for the language. Examine this semantics. Can it be used to determine the denotation of a Scheme program consisting entirely of the numeral 42?
  2. Here is the concrete syntax, in Backus-Naur form, of a unary expression in C:
    <unary-expression> ::= <postfix-expression>
                         | ++ <unary-expression>
                         | -- <unary-expression>
                         | <unary-operator> <cast-expression>
                         | sizeof <unary-expression>
                         | sizeof <type-name>
    
    Construct a PLAI-style type definition that expresses the corresponding abstract syntax.
  3. In Krishnamurthi's parser (page 8), what will happen if the S-expression that the caller supplies contains a syntax error? If an error message is generated, is it of any use to the AE programmer?

January 21, 2008

  1. What are the "big ideas" of this course -- the topics that we'll deal with most intensively this semester?
  2. What are some of the distinctive features of our textbook? In other words, how does it differ from other textbooks dealing with programming languages generally?
  3. What are the advantages and disadvantages of an interpreter-based course in programming languages?