Re: Closures versus objects
"Robby Findler" <[email protected]>
| Newsgroups | gmane.comp.lang.lightweight |
|---|---|
| Message-ID | <[email protected]> |
Just let me prefix this by saying that I'm no Java advocate. I just think there are some cheap shots below. On 2/21/07, Joe Marshall <[email protected]> wrote: > 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). Most of those lines would be in a functional version, but they would be in different places. > Can I mention, too, that Java's `if ... else ...' construct breaks the > substitution > model? We know how to do operational semantics with state and statements. > 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 > >