Thinking in FP vs OOP for large scale apps => Re: [plt-scheme] Imperative programming : missing the flow

From: Matthias Felleisen (matthias at ccs.neu.edu)
Date: Wed May 16 08:14:34 EDT 2007

YC, over the past semester I have written a small (6+ Kloc)   
distributed game with a graphical display (but no real interface). I  
wrote it in the FP style I advocated in my ECOOP keynote in 2004.

An intern from ENS and Sam TH have already ported the code to Typed  
Scheme (in one day). We're hoping to report on that experience in a  
paper. As others have pointed out 'types vs dynamically safe' and  
'oop vs fp' are distinct questions.

I am in the process of rewriting this project using (lib "class.ss"),  
also following the guidelines advocated in this talk. After you have  
understood basic FP (as in HtDC), you need to move to a modular style  
of programming:

(module stack mzscheme
   (define-struct stack (list))

   (define (create-stack card) (make-stack (list card)))

   (define (stack-push this nu) ...)
   (define (stack-pop! this) ...)
   (define (stack-size this) ...)

   (require (lib "contract.ss"))
   (provide/contract
     [create-stack (-> card/c stack?)]
     [stack-push   (-> stack? card/c stack?)]
     [stack-pop!   (-> stack? stack?)]
     [stack-size   (-> stack? natural-number/c)]))

And yes, this is how we teach the end of HtDP these days.

Rewriting this kind of module into a module using class-oriented  
programming is straightforward:

(module stack mzscheme
   (require (lib "class.ss"))

   (define (create-stack card) (make-object stack% (list card)))

   (define stack%
     (class* object% (stack<%>)
      (init-field content)
      (super-new)
      (define/public (push nu) ...)
      (define/public (pop!) ...)
      (define/public (size) ...)))

   (require (lib "contract.ss"))

   (define stack? (flat-named-contract "stack" (lambda (x) (is-a? x  
stack<%>))))

   (provide/contract
     [create-stack (-> card/c stack?)])))

If you paid close attention, you'll see that I use an interface --  
stack<%> -- in addition to the class. I collect all these public  
interfaces in a separate module -- dubbed interfaces.ss -- which I  
require in each module of the project. It is easy to reason about the  
project as a whole by just looking at this one module.

I think that others who plainly stated not to see a difference  
between FP and OOP may just be thinking of this kind of programming.

My experience has been terrific. I spent one real workday so far and  
the project is basically up and running. The interface-oriented style  
of class-based programming has several advantages over plain modular  
programming. I intend to report more details in an essay/paper one  
day. (Sneak preview: inheritance plays almost no role.)

Eventually I intend to rewrite the code into the new unit-based  
style. I conjecture that units and classes based code bases will be  
nearly isomorphic.

Last but not least: I have used pure forms of FP and applicative OOP  
here. Some other modules use set!-y style, and the conversion was  
identical.

-- Matthias












On May 15, 2007, at 5:59 PM, YC wrote:

> Hi all -
>
> thanks for the excellent discussion. I have a related question, but  
> from probably the opposite perspective, given I come from OOP world:
>
> How to think in FP rather than OOP, especially for large scale apps?
>
> This question is of course general/meta, but would love to hear  
> commentary/experiences on how to fully think in FP rather than OOP,  
> and also perhaps, how not to think in types (i.e. the question is  
> about Scheme's "lack" of types so to speak).
>
> OOP to me provides a nice way to organize related code and data  
> together, in a stronger relation than module.  The OOP world I am  
> familiar with (i.e. C#, etc) also has strong notion of typing.   
> Combined together, one can ensure that the data in an object is  
> "type safe", i.e. validation/conversion has taken place as part of  
> object construction.  Once an object is constructed, it is safe to  
> pass around, etc., and we know that class methods are always safe  
> to call on an object of the type.
>
> FP on the other hand seems less tightly coupled, and that means a  
> couple of things to me:
> I have to explicitly pass the object around
> I have to type check the object if the function can only handle  
> some specific types, i.e. (cond ((type-a? obj) ...) ...)
> These things are not big deal for small applications, but for  
> bigger apps it seems to be quite cumbersome to have to do all these  
> different checks everywhere.  And that gives me a feeling that I  
> don't have it right, yet.
>
> I came across the notion that classes/objects can be modeled by  
> closures and indeed had some fun trying to write my own class  
> generator as an exercise, but then I have to wonder if I am missing  
> a big picture somewhere - i.e. would I do "better" (i.e. fewer  
> lines of code, higher conceptual abstraction) with raw FP rather  
> than OOP for large scale app?  Would problems like object- 
> relational mapping go away (a very cumbersome procedure)? etc.
>
> So perhaps, as an example - an enterprise app with hundreds of  
> tables in a database.  In OOP each table would be mapped to a  
> class, and the code mostly deals with converting user input to  
> objects that are then translated to query for the database.  The  
> business logic then concerns with manipulation of the objects  
> between the layers.  It is obvious that in such a system there are  
> a ton of boiler plate code, and that's what Java is famous for.
>
> How does FP model the above example differently?
>
> Any input is appreciated ;)  Thanks,
> yinso
>
> On 5/12/07, Anton van Straaten < anton at appsolutions.com> wrote:John  
> Clements wrote:
> > I'm teaching Java to first-year students, and we're about to walk  
> off  a
> > cliff and start programming imperatively.  I've done this   
> transition
> > several times now, and it occurs to me that what's most   
> irritating to
> > me about the imperative style is the lack of "flow".
> >
> > What do I mean by flow?  Simply that each expression produces a   
> result
> > that is used by another expression.  In imperative  programming, by
> > contrast, I feel that I have a bunch of statements  lying on the  
> floor,
> > and that their order is both important and  difficult to explain  
> clearly.
>
> Perhaps this is too obvious to mention in the present company, but  
> since
> you didn't mention it, the world-passing (monadic) view of imperative
> programming might provide a useful perspective.  It provides an easily
> understood theory (at a high level) of imperative programming, which
> lets you compare imperative programs to functional programs using a
> common model.  (Admittedly, the model is skewed in favor of the
> functional approach, but I'm guessing you won't object to that.)
>
> The resulting explanations end up not being all that different from
> those that have already been given, but introducing "the world" as a
> first-class feature of the explanatory model helps to discuss and  
> reason
> about the issues.
>
> For example, the world-passing perspective explains why the imperative
> puzzle pieces (statements) that Joe described are perfect squares: the
> only functional/algebraic dependency between statements is "the  
> world",
> so there's no way to fit statements together based on what kind of  
> value
> they consume and produce -- they all consume and produce a version of
> the world.
>
> Another point is that every statement in an imperative program can
> change the value of any currently visible variable in the world.  In
> contrast, a functional program can only "change" the value of  
> variables
> at function application time.  So every single imperative statement is
> like a function in its own right, which complicates reasoning.
>
> If the students can handle a functional model of an imperative program
> looking something like:
>
>    ((lambda (world) stmt-n)
>     ((lambda (world) ...)
>      ((lambda (world) stmt-1)
>       '())))
>
> ...then there are quite a few concrete points and comparisons that  
> could
> be made with it.  It's also good preparation for teaching monads  
> later... :)
>
> Anton
>
> _________________________________________________
>   For list-related administrative tasks:
>    http://list.cs.brown.edu/mailman/listinfo/plt-scheme
>
> _________________________________________________
>   For list-related administrative tasks:
>   http://list.cs.brown.edu/mailman/listinfo/plt-scheme



Posted on the users mailing list.