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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.