CSC 123/252 Nightmare Test WITH SOLUTIONS You partied when you should be studying and now you suddenly realize that you forgot that the programming languages final was TODAY! And the problems looks hard too, because you didn't study! 1. Explain what, if anything, is wrong with the following C# interface: interface I { A f(B x); B g(B x); } B appears as a return type as well as an argument type, and therefore cannot be contravariant. in B means B can only be an argument type. 1b. Explain what, if anything, is wrong with the following code: using System.Collections.Generic; //... List N = new List; // List is similar to ArrayList in Java The type variable in T in List cannot be covariant because there are procedures that both SET and GET the values from the list: T must appear as both a return type (codomain type) and argument type (domain type). 2. Consider the following F# code: type 'a lst = Nomore | More of ('a * 'a lst); let rec sum = function | Nomore -> 0 | More(x,cdr) -> x + sum(cdr);; 2a. Write a tail-recursive version of the sum function, or use a loop: Using a loop is not recommended: discriminated union really needs pattern matching let rec isum ax = function // ax should be 0 | Nomore -> ax | More(x,d) -> x + isum (ax+x) d;; // note ()s for curried function or let sum n = let mutable ax = 0 let rec isum = function | Nomore -> (); // type unit (void return) | More(x,d) -> ax <- ax+x isum d;; 3. Consider the following program interface expr // interface for abstract expression { T accept(exprvisitor v); // polymorphic function } interface exprvisitor // interface for abstract visitor { T visit(intnode e); T visit(plusnode e); T visit(timesnode e); } class intnode : expr // integer expreesion { internal int val; public intnode(int v) {val = v; } public T accept(exprvisitor v) { return v.visit(this); } } // intnode class plusnode : expr // + operator expression { public expr left, right; public plusnode(expr l, expr r) { left=l; right=r; } public T accept(exprvisitor v) { return v.visit(this); } } // plusnode class timesnode : expr // * operator expression { public left, right; public timesnode(expr l, expr r) { left=l; right=r; } public T accept(exprvisitor v) { return v.visit(this); } } // timesnode class evalvisitor : exprvisitor { public int visit(intnode e) { return e.val; } public int visit(plusnode e) { return e.left.accepth(this)+e.right.accept(this); } public int visit(timesnode e) { return e.left.accepth(this)*e.right.accept(this); } } Write an equivalent program in F# using a discriminated union. Hint: the space below should be enough... type expr = Num of int | Plus of expr*expr | Times of expr*expr;; let rec eval = function | Num(n) -> n | Plus(a,b) -> eval(a) + eval(b) | Times(a,b) -> eval(a) * eval(b);; 4. Assume you have: class A { void f(int x) {...} } class B { void g() { A a = new A(); a.f(1); } } The professor then says: "write an aspect with an advice that captures both the instance of of A and the instance of B involved in a call to A.f(int) from within B.g(). Print a message around the call so as to trace it." After fighting off a panic attack you were able to muster the following: void around(A a, B b, int x) : ?? (sorry prof, my head hurts and I forgot!) { System.out.println("f is being called on object "+a+ " from object "+b); System.out.println("with argument "+x); proceed(a,b,x); } You know it's not correct, but hopefully with some further thought you can figure out how to write the appropriate pointcut expression for the advice instead of the pathetic excuse you have now ... 5. Explain what is wrong with the following advice (given class A above) void around(A m, int x) : call(void A.f(int)) && args(x) && target(m) { m.f(x+1); } 5b. Show how to avoid the problem above: Several solutions: 1. call proceed(m,x+1) instead of m.f(x+1) 2. using && !adviceexecution 5. You wake up and realize that it's only a nightmare. The real test is next week. Reschedule the party.