Re: Closures versus objects
"Joe Marshall" <[email protected]>
| Newsgroups | gmane.comp.lang.lightweight |
|---|---|
| Message-ID | <[email protected]> |
On 2/21/07, Robby Findler <[email protected]> wrote: > Just let me prefix this by saying that I'm no Java advocate. I just > think there are some cheap shots below. It's not *my* fault that Java is so easy to take shots at. > 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. Perhaps, but is the placement a matter of taste or necessity. And is the Java placement better or worse? I'll argue that what you *want* to do is specialize the NIL class (that is, the class containing the singleton instance NULL, not the empty class), but you cannot because NULL is weird. > > 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. *You* know, but do you want to introduce that to your students? While it is true that I know as well, I much prefer to use a construct I can reason about more simply: static Chain makeChain(Color c, int len){ return (len == 1) ? new EndChain(c) : new ConsChain(c, makeChain(c, len-1)); } The syntax is a bit awkward, but at least substitution works. (Hey, if you've chosen Java, complaining about awkward syntax is a little silly) > > > 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 > > > > > -- ~jrm