CS 330 Home Schedule Resources

Assignment 11
Laziness and Currying;
Message Passing Concurrency

Estimated time: 3 hours

(In Haskell and Oz)

Objectives:

Exercises

Due Nov 5 by 6am, except for #4, which is due Nov 12th by 6am.
Haskell is available on the lab machines by typing 'ghci' at the command prompt. From the Haskell prompt, you can load a file by typing ':load myfile.hs' (without the quotes). Type Control-D to quit.

[For these exercises, you may find it useful to examine the code snippets in HaskellTest.hs here, as well as glancing at this web tutorial on Haskell. I wouldn't spend more than 30 minutes, tops, on the tutorial, though. -Max]

  1. Laziness -- 30 points
    Write a function pows(N X) in Haskell which returns a list of the first N powers of X, starting at 1. So the call pows(10 3) evaluates to [1,3,9,27,81,243,729,2187,6561,19683]. Note: in Haskell, the "!!" operator is equivalent to List.nth in Mozart: xs!!n gives the nth element of the list xs.
    Show your code and answer the following questions:

    A.i. First execute "let x = (pows 100000000 3)". What is the type of x? What is the type of x!!1000? In terms of big-O, how much time does this operation take, given that it is lazy?

    A.ii. Approximately how big is x in a logical sense, e.g. the total number of digits in all its members, independent of how it is implemented or created? How much memory do you suppose it actually occupies on your computer before any elements are accessed or used? Comment on the disparity or lack thereof.

    B. Assume you have a function foo to create an infinite list of lists, where each inner list in the nth position has n elements. For example, the first five elements of this list might be
    [[1], [2,2], [3,3,3], [4,4,4,4], [5,5,5,5,5]].

    i. How much time (in terms of big-O) does it take to execute let y=foo? ii. Then how much time does it take to execute y!!n? How would the answers to these two questions be different if foo eagerly created a finite list of length n?

  2. Currying -- 35 points

    In Haskell, "*" is just syntactic sugar for a binary function which multiplies two numbers (mult(x,y) = x*y). This binary function can be curried, as per your book's example on page
    311. Currying is a way of binding an n-ary function's parameter to a given value, yielding an (n-1)-ary function. Thus, currying (x*y) with x===2 yields the function (2*y).

    Consider the following Haskell code:
    (Note: this code can be entered interactively. To load it from a file, drop the 'let' from each line.

    let curry1 = (* 2)
    let curry2 = (\x -> 2*x)
    let a = curry1 7
    let b = curry2 7
    let c = (* 2) 7
    let d = (2 * 7)
    let exec = map curry1


    1. What difference, if any, is there between curry1 and curry2? What about a, b, c, and d? What, in your own words, is the type of exec? What happens if you execute "exec [1,2,3,4]"? Why?

    2. Write a function Mult in Mozart/Oz equivalent to mult as defined above. Write another function CMult which curries Mult, so that {CMult 2} is equivalent to curry1 above. Your code should satisfy the following constraint:

    for A in [1 3 7 8] do
      for B in [2 4 10 ~20] do
        {Mult A B} = A * B    % will fail with TELL error if false
        {{CMult A} B} = A * B
      end
    end

    Show your code. Is Haskell's "*" operator more like Mult or CMult? For extra "coolness" points, comment on the level of linguistic support for currying in Haskell vs. Mozart vs. Java (or C++). (Guru points: implement CMult in C++, so "(CMult(2))(7)" returns 14.)

  3. Message-passing concurrency. Write a program with two clients that independently send messages to a server. Each message should be a pair of the client ID (an integer) and an atom describing which operation the client would like the server to perform. The server should accept the messages 'add' and 'mult', and perform the respective operation on two arguments: the client ID and the integer 11. The server should output the following tuple to a stream after processing each client's request: clientID#op#result.

    Write a second server that cumulatively adds up all the results of the client requests, and outputs the tuple clientID#op#result#sum on a stream. (Hint: use the Port Object abstractions defined in 5.2.1 of the textbook.)

  4. Feedback.