Re: Closures versus objects
"Joe Marshall" <[email protected]>
| Newsgroups | gmane.comp.lang.lightweight |
|---|---|
| Message-ID | <[email protected]> |
What is sad is that the standard Java libraries actively discourage functional programming. I needed a pure functional `set' implementation and had to roll my own because there was no way to add an element to a set without mutating it! (I don't recall there being mutation axioms in ZF set theory.) Also sad is your 26-line definition of NULL (the end of the chain). Can I mention, too, that Java's `if ... else ...' construct breaks the substitution model? ok, I'll stop now.... On 2/21/07, John Clements <[email protected]> wrote: > > On Feb 21, 2007, at 9:38 AM, Joe Marshall wrote: > > > On 2/21/07, Spencer Schumann <[email protected]> wrote: > >> Of course, without first class functions, programming in a functional > >> style with Java is painful. Trying to program in a *pure* functional > >> style in Java would be downright insane. > > > > Verbose and annoying, yes. Insane? Well, I've done some and *I* > > didn't think > > it was that bad, but then again it's me..... > > It's not bad at all. Here's the sample solution for my students' > fifth project, which creates a small interactive puzzle game where > keypresses move beads around two interlocking rings. The students' > code (i.e., the code below) is purely functional, though the > (ProfessorJ) libraries they use are implemented in a stateful way. > > FWIW, here's the problem statement. They didn't use mutation in this > assignment because they don't know how. > > http://www.csc.calpoly.edu/~clements/csc102-wi07/Assignments/ > program5.html > > > > > import colors.*; > import geometry.*; > import draw.*; > > // new things: public/private > // static-ish fields for geometry > // mega-accumulator method on Chain > // errors > // easier to reverse twice? > > > // Represent a chain of beads > interface Chain { > boolean drawLoop(Canvas c, boolean isLeft, int loc); > > Color findFirst(); > Color findLast(); > > Chain addToEnd(Color newLast); > > Chain shiftLeft(Color newLast); > Chain shiftRight(Color newFirst); > > } > > > // MT > class EndChain implements Chain { > Color only; > > public boolean drawLoop(Canvas c, boolean isLeft, int loc){ > return c.drawDisk(new BeadGeometry().findPosn(loc,isLeft),new > BeadGeometry().dotRadius,this.only); > } > > public Color findFirst(){ > return this.only; > } > > public Color findLast(){ > return this.only; > } > > public Chain addToEnd(Color newLast){ > return new ConsChain(this.only, new EndChain(newLast)); > } > > public Chain shiftLeft(Color newLast){ > return new EndChain(newLast); > } > > public Chain shiftRight(Color newFirst){ > return new EndChain(newFirst); > } > > public EndChain(Color only) { > this.only = only; > } > } > > // ConsChain > class ConsChain implements Chain { > Color first; > Chain rest; > > public boolean drawLoop(Canvas c, boolean isLeft, int loc){ > return c.drawDisk(new BeadGeometry().findPosn(loc,isLeft),new > BeadGeometry().dotRadius,this.first) > && rest.drawLoop(c,isLeft,loc+1); > } > > public Color findFirst(){ > return this.first; > } > > public Color findLast(){ > return rest.findLast(); > } > > public Chain addToEnd(Color newLast){ > return new ConsChain(this.first,this.rest.addToEnd(newLast)); > } > > public Chain shiftLeft(Color newLast){ > return this.rest.addToEnd(newLast); > } > > public Chain shiftRight(Color newFirst) { > return new ConsChain(newFirst,this.rest.shiftRight(this.first)); > } > > ConsChain(Color first, Chain rest) { > this.first = first; > this.rest = rest; > } > } > > > class BeadGeometry{ > > int posns = 32; // must be even, for simplicity > int radius = 100; > int inChainNum = 5; // must be odd if posns is even > int dotRadius = 10; > int leftRightPad = dotRadius + 30; > int topBottomPad = dotRadius + 30; > > double incrAng = (Math.PI * 2) / posns; > > int overlapPosn = (inChainNum - 1) / 2 + 1; > double overlapAngle = overlapPosn * incrAng; > int centerSpacing = (int)Math.round (Math.cos(overlapAngle) * > radius * 2); > > int leftCenterX = radius + leftRightPad; > int rightCenterX = leftCenterX + centerSpacing; > int centerY = radius + topBottomPad; > > // draw a bead in one location of one of the two circles > Posn findPosn(int location, boolean isLeft) { > double thisAng = location * incrAng; > int centerX = (isLeft ? leftCenterX : rightCenterX); > return new Posn(centerX + (int)Math.round(Math.cos(thisAng) * > radius), > centerY - (int)Math.round(Math.sin(thisAng) * > radius)); > } > > > } > // represent a state of the bead world > class State { > Color topX; > Color botX; > Chain outL; > Chain inL; > Chain inR; > Chain outR; > > State rotateLeftCounterClockwise (){ > return new State(inR.findLast(),outL.findLast(),outL.shiftRight > (topX),inL,inR.shiftRight(botX),outR); > } > > State rotateLeftClockwise (){ > return new State(outL.findFirst(),inR.findFirst(),outL.shiftLeft > (botX),inL,inR.shiftLeft(topX),outR); > } > > State rotateRightCounterClockwise (){ > return new State(outR.findLast(),inL.findLast > (),outL,inL.shiftRight(topX),inR,outR.shiftRight(botX)); > } > > State rotateRightClockwise (){ > return new State(inL.findFirst(),outR.findFirst > (),outL,inL.shiftLeft(botX),inR,outR.shiftLeft(topX)); > } > > // draw the beads on the canvas > boolean draw(Canvas c){ > BeadGeometry bg = new BeadGeometry(); > return outL.drawLoop(c,true,new BeadGeometry().overlapPosn + 1) > && inR.drawLoop(c,true,1 - new BeadGeometry().overlapPosn) > && outR.drawLoop(c,false,new BeadGeometry().overlapPosn + (new > BeadGeometry().posns / 2) + 1) > && inL.drawLoop(c,false,1 - new BeadGeometry().overlapPosn + > (new BeadGeometry().posns / 2)) > && c.drawDisk(new BeadGeometry().findPosn(new BeadGeometry > ().overlapPosn,true),new BeadGeometry().dotRadius,topX) > && c.drawDisk(new BeadGeometry().findPosn(-new BeadGeometry > ().overlapPosn,true),new BeadGeometry().dotRadius, botX > ); > } > > boolean erase(Canvas c){ > return true; > } > > State(Color topX, Color botX, Chain outL, Chain inL, Chain inR, > Chain outR) { > // check the lengths of these lists > this.topX = topX; > this.botX = botX; > this.outL = outL; > this.inL = inL; > this.inR = inR; > this.outR = outR; > } > } > > // represent a bead world > public class BeadWorld extends World { > State theState; > > // draw the state > public boolean draw () { > return theState.draw(theCanvas); > } > > // don't do anything special on a tick of the counter > public World onTick(){ > return this; > } > > // when the user presses a key, rotate the rings > public World onKeyEvent (String in){ > if (in.equals("a")) { > return new BeadWorld(theState.rotateLeftCounterClockwise()); > } else if (in.equals("z")) { > return new BeadWorld(theState.rotateLeftClockwise()); > } else if (in.equals("k")) { > return new BeadWorld(theState.rotateRightClockwise()); > } else if (in.equals("m")) { > return new BeadWorld(theState.rotateRightCounterClockwise()); > } else > return this; > } > > // in the bead game, erasing is unnecessary. > public boolean erase(){ > return true; > } > > BeadWorld(State theState){ > this.theState = theState; > } > } > > class WorldExamples{ > Color blue = new Blue(); > Color red = new Red(); > Color green = new Green(); > State s1 = new State(green,green,makeChain(blue,25), > this.makeChain(green,5),this.makeChain > (green,5),this.makeChain(red,25)); > BeadWorld bw = new BeadWorld(s1); > Chain testChain = new ConsChain(new Blue(),new ConsChain(new Yellow > (),new EndChain (new Red()))); > > boolean testEverything(){ > boolean t1 = check testChain.shiftLeft(new Green()) > expect new ConsChain(new Yellow(),new ConsChain(new Red(), new > EndChain(new Green()))); > boolean t2 = check testChain.shiftRight(new Green()) > expect new ConsChain(new Green(),new ConsChain(new Blue(),new > EndChain(new Yellow()))); > boolean t3 = bw.bigBang(600,600,1); > return t1 && t2; > } > > static Chain makeChain(Color c, int len){ > if (len == 1) > return new EndChain(c); > else > return new ConsChain(c,makeChain(c,len-1)); > } > } > -- ~jrm