[plt-scheme] "appending" to classes rather than extending

From: Rob Hunter (rob.hunter at gmail.com)
Date: Wed Mar 5 18:24:40 EST 2008

Even though I'd rather be in units land than classes land*, I like
this solution better because I don't have to enumerate all the
identifiers that I *don't* care about when I'm implementing my
extension.  The reason this is important is that I'm writing macros to
abstract over all of the implementation details for the benefit of the
users of my library, and it seems a lot more complicated to
programmatically generate all these exports with macros than to not
have to.

Thanks for your help, Matthias.

--rob

* Why no love for the classes? The way I see it, you pay a price to
move into classes land in Scheme.  Suddenly your functions aren't
functions anymore -- they're methods which require a "send" instead of
just a left paren.  And it's more than just the aesthetic appeal of
using pure functions -- it's actually a mis-match for what I'm trying
to achieve: I want my users to think they are just getting ordinary
Scheme functions (where these functions are the result of base
implementations with potential overrides to functionality that they
made)**.  And I'm just using classes as a kind of "extensible
singleton pattern" hack, and not really using the OOP stuff like
inheritance and fields and what not.  Under more "ordinary"
circumstances, I wouldn't use OOP in such a case.

** What I'm actually working on is a web app library, where the
implementations of the functions in the library depend on a runtime
value (i.e., the web server request). So, e.g., current-user might be
a function in the library, and it *won't* require a request as an
argument -- it's "built-in" dynamically.  I also want clients of the
library to be able to override any of the default implementations of
the library functions.  This library, btw, is my attempt at striking a
balance in the library vs framework war.




On Wed, Mar 5, 2008 at 2:53 PM, Matthias Felleisen <matthias at ccs.neu.edu> wrote:
> Let's do it with modules:
>
>  (module foo mzscheme
>    (require scheme/class)
>    (provide foo%)
>
>    (define foo%
>      (class object% (init-field count)
>        (super-new)
>        (define/public (bar)
>          (if (<= count 0)
>              (printf "done\n")
>              (begin
>                (printf "bar\n")
>                (set! count (- count 1))
>                (send this baz)))))))
>
>  (module foo-extended scheme
>    (require scheme/class
>             'foo)
>    (provide (rename-out (foo-extended% foo%)))
>
>    (define foo-extended%
>      (class foo% (inherit-field count)
>        (super-new)
>        (define/public (baz)
>          (if (<= count 0)
>              (printf "done\n")
>              (begin
>                (printf "baz\n")
>                (set! count (- count 1))
>                (send this bar)))))))
>
>  (require 'foo-extended)
>  (send (new foo% [count 4]) bar)
>
>  This uses inheritance to simulate appending. Do you like this better?
>  -- Matthias
>
>
>
>
>
>
>
>
>  On Mar 5, 2008, at 5:36 PM, Rob Hunter wrote:
>
>  > Yeah, seems to just work in Ruby (see code below).
>  >
>  > It seems strange to me that I can't just "re-export" the definition
>  > of foo:
>  >
>  > (define-unit extension-unit%
>  >   (import sig^)
>  >   (export sig^)
>  >
>  >   (define (bar)
>  >     "appended bar impl"))
>  >
>  > But unfortunately? it gives me:
>  >
>  > define-unit: import foo is exported in: (define-unit extension-unit%
>  > (import sig^) (export sig^) (define (bar) "appended bar impl"))
>  >
>  > Here's that Ruby code:
>  >
>  > class Foo
>  >   def initialize(count)
>  >     @count = count
>  >   end
>  >
>  >   def bar
>  >     if @count <= 0
>  >       print "done\n"
>  >     else
>  >       print "bar\n"
>  >       @count = @count - 1
>  >       baz
>  >     end
>  >   end
>  > end
>  >
>  > class Foo
>  >   def baz
>  >     if @count <= 0
>  >       print "done\n"
>  >     else
>  >       print "baz\n"
>  >       @count = @count - 1
>  >       bar
>  >     end
>  >   end
>  > end
>  >
>  > Foo.new(4).bar =>
>  > bar
>  > baz
>  > bar
>  > baz
>  > done
>  >
>  >
>  >
>  >
>  > On Wed, Mar 5, 2008 at 2:25 PM, Matthias Felleisen
>  > <matthias at ccs.neu.edu> wrote:
>  >>
>  >>  Yes. Since you want to talk about both base:bar and extension:bar in
>  >>  one unit, I don't see how to do it w/o some name shuffling.
>  >>
>  >>  BTW, happens if the foo and bar methods are (mutually) recursive in
>  >>  Ruby. Does this "just work"?
>  >>
>  >>  -- Matthias
>  >>
>  >>
>  >>
>  >>
>  >>
>  >>
>  >>
>  >>
>  >>  On Mar 5, 2008, at 4:51 PM, Rob Hunter wrote:
>  >>
>  >>> Thanks, Matthias.  Below is my interpretation of your suggestion of
>  >>> using units to get this effect.  The only parts of my solution
>  >>> that I
>  >>> don't like is how I had to prefix the import in extension-unit%, and
>  >>> how I had to explicitly define foo for export even though I didn't
>  >>> want to override it.  Surely there's a way to avoid both of those
>  >>> things...
>  >>>
>  >>> Thanks,
>  >>> Rob
>  >>>
>  >>> (define-signature sig^ (foo bar))
>  >>>
>  >>> (define-unit base-unit%
>  >>>   (import)
>  >>>   (export sig^)
>  >>>
>  >>>   (define (foo)
>  >>>     "foo base impl")
>  >>>
>  >>>   (define (bar)
>  >>>     "bar base impl"))
>  >>>
>  >>> (define-unit extension-unit%
>  >>>   (import (prefix base: sig^))
>  >>>   (export sig^)
>  >>>
>  >>>   (define foo base:foo)
>  >>>
>  >>>   (define (bar)
>  >>>     "appended bar impl"))
>  >>>
>  >>> (define-compound-unit merged-unit%
>  >>>   (import)
>  >>>   (export RESULT)
>  >>>   (link [((BASE : sig^)) base-unit%]
>  >>>         [((RESULT : sig^)) extension-unit% BASE]))
>  >>>
>  >>>
>  >>> REPL interaction:
>  >>>> (define-values/invoke-unit merged-unit% (import) (export sig^))
>  >>>> (foo)
>  >>> "foo base impl"
>  >>>> (bar)
>  >>> "appended bar impl"
>  >>>
>  >>>
>  >>>
>  >>>
>  >>>
>  >>>
>  >>> On Wed, Mar 5, 2008 at 12:58 PM, Matthias Felleisen
>  >>> <matthias at ccs.neu.edu> wrote:
>  >>>>
>  >>>>  You could achieve this kind of effect (via units) but re-
>  >>>> definitions
>  >>>>  in a module (and that's what this boils down to) of the same name
>  >>>> are
>  >>>>  frowned upon in Scheme. -- Matthias
>  >>>>
>  >>>>  P.S. Re-definitions at the top-level have been around for a LONG
>  >>>> time
>  >>>>  and there is a reason why Schemers are moving away from that
>  >>>> approach.
>  >>>>
>  >>>>
>  >>>>
>  >>>>  On Mar 5, 2008, at 3:41 PM, Rob Hunter wrote:
>  >>>>
>  >>>>> Hi all,
>  >>>>>
>  >>>>> I'm looking to be able to append a few methods to a class.  In
>  >>>>> Ruby, I
>  >>>>> can do something like this:
>  >>>>>
>  >>>>> class Foo
>  >>>>>   def bar
>  >>>>>     ...
>  >>>>>   end
>  >>>>>   ...
>  >>>>> end
>  >>>>>
>  >>>>> class Foo
>  >>>>>   def baz
>  >>>>>     ...
>  >>>>>   end
>  >>>>> end
>  >>>>>
>  >>>>> and both bar and baz will be a part of Foo (these two class defs
>  >>>>> could
>  >>>>> be in the same file, or in two different files that are both
>  >>>>> loaded).
>  >>>>> Anything like this in the PLT Scheme class system?
>  >>>>>
>  >>>>> Thanks,
>  >>>>> Rob
>  >>>>> _________________________________________________
>  >>>>>   For list-related administrative tasks:
>  >>>>>   http://list.cs.brown.edu/mailman/listinfo/plt-scheme
>  >>>>
>  >>>>
>  >>
>  >>
>
>


Posted on the users mailing list.