extending a core class cleanly [noobish alert]

Discussion in 'Ruby' started by Fearless Fool, Jul 5, 2010.

  1. Assume I'd like to create a statistics package designed to work on
    Arrays [but see P.S.]. One way to do this would be to create functions
    that take an array as an argument:

    def mean(array)
    return NaN unless array && array.size > 0
    array.reduce(0.0) {|accum, val| accum + val} / array.size
    end

    ... but that's so old school. What I'd really like is to *cleanly*
    extend the Array class so you can operate on it directly, as in:

    >> [1, 2, 3].mean

    => 2.0

    I'm sure that it's considered bad style to simply open the Array class
    and extend it, since that can lead to unexpected user surprises:

    class Array
    def mean
    return NaN unless self.size > 0
    self.reduce(0.0) {|accum, val| accum + val} / self.size
    end
    end

    My hunch is that the stats functions should be wrapped in a module and
    then included (extended?) when desired. But despite reading the
    tutorials and docs, I'm not sure what's considered the stylistically
    correct way (and the syntax) to package that up.

    Comments? Pointers?

    Thanks!

    - ff

    [P.S.: Before you tell me about available stats packages, etc, note that
    I'm only using statistics as an example. My real question is about
    extending a class...]
    --
    Posted via http://www.ruby-forum.com/.
    Fearless Fool, Jul 5, 2010
    #1
    1. Advertising

  2. On Monday, July 05, 2010 03:30:44 pm Fearless Fool wrote:
    > Assume I'd like to create a statistics package designed to work on
    > Arrays [but see P.S.]. One way to do this would be to create functions
    > that take an array as an argument:
    >
    > def mean(array)
    > return NaN unless array && array.size > 0
    > array.reduce(0.0) {|accum, val| accum + val} / array.size
    > end
    >
    > ... but that's so old school. What I'd really like is to *cleanly*
    >
    > extend the Array class so you can operate on it directly, as in:
    > >> [1, 2, 3].mean

    >
    > => 2.0
    >
    > I'm sure that it's considered bad style to simply open the Array class
    > and extend it, since that can lead to unexpected user surprises:
    >
    > class Array
    > def mean
    > return NaN unless self.size > 0
    > self.reduce(0.0) {|accum, val| accum + val} / self.size
    > end
    > end


    What kind of surprises?

    > My hunch is that the stats functions should be wrapped in a module and
    > then included (extended?) when desired. But despite reading the
    > tutorials and docs, I'm not sure what's considered the stylistically
    > correct way (and the syntax) to package that up.


    Simple enough:

    module MyModule
    def mean
    ...
    end
    end

    Then, you can always do this:

    [].extend MyModule

    Or this:

    class Array
    include MyModule
    end

    Then, probably, provide a file people can 'require' that'll do the inclusion
    for them, maybe even make that the default.

    It might also be nice to provide a way to do this on Enumerables, though that
    would be less efficient if you know you have an array. For example:

    def mean
    size = 0
    count = inject(0){|accum, val| size+=1; accum+val}
    count / size.to_f
    end
    David Masover, Jul 5, 2010
    #2
    1. Advertising

  3. Re: extending a core class cleanly

    David Masover wrote:

    > What kind of surprises?


    Coming from conventional programming environments, one might be
    surprised when a core class suddenly acquires new methods -- e.g.
    Rails/ActiveSupport's extensions to String -- so I wanted to make sure I
    was packaging such extensions properly.

    > Simple enough:
    > [snip]


    Perfect -- I see how that works now. Thank you.

    > Then, probably, provide a file people can 'require' that'll do the
    > inclusion for them, maybe even make that the default.


    ... or if I was feeling adventuresome, perhaps an autoload?

    > It might also be nice to provide a way to do this on Enumerables, though
    > that would be less efficient if you know you have an array.


    Good catch. If I *do* implement a stats package, I'll think about
    making it ambidextrous.

    Thanks again.

    - ff
    --
    Posted via http://www.ruby-forum.com/.
    Fearless Fool, Jul 6, 2010
    #3
  4. On Mon, Jul 5, 2010 at 11:22 PM, David Masover <> wrote:
    > On Monday, July 05, 2010 03:30:44 pm Fearless Fool wrote:
    >> Assume I'd like to create a statistics package designed to work on
    >> Arrays [but see P.S.]. =A0One way to do this would be to create function=

    s
    >> that take an array as an argument:
    >>
    >> =A0 def mean(array)
    >> =A0 =A0 return NaN unless array && array.size > 0
    >> =A0 =A0 array.reduce(0.0) {|accum, val| accum + val} / array.size
    >> =A0 end
    >>
    >> ... but that's so old school. =A0What I'd really like is to *cleanly*
    >>
    >> extend the Array class so you can operate on it directly, as in:
    >> =A0 >> [1, 2, 3].mean
    >>
    >> =A0 =3D> 2.0
    >>
    >> I'm sure that it's considered bad style to simply open the Array class
    >> and extend it, since that can lead to unexpected user surprises:
    >>
    >> =A0 class Array
    >> =A0 =A0 def mean
    >> =A0 =A0 =A0 return NaN unless self.size > 0
    >> =A0 =A0 =A0 self.reduce(0.0) {|accum, val| accum + val} / self.size
    >> =A0 =A0 end
    >> =A0 end

    >
    > What kind of surprises?
    >
    >> My hunch is that the stats functions should be wrapped in a module and
    >> then included (extended?) when desired. =A0But despite reading the
    >> tutorials and docs, I'm not sure what's considered the stylistically
    >> correct way (and the syntax) to package that up.

    >
    > Simple enough:
    >
    > module MyModule
    > =A0def mean
    > =A0 =A0...
    > =A0end
    > end
    >
    > Then, you can always do this:
    >
    > [].extend MyModule


    I agree, this is the less dangerous thing to do: just extend the
    specific array instances with your functionality. That way, all other
    arrays are left intact.

    Jesus.
    Jesús Gabriel y Galán, Jul 6, 2010
    #4
  5. Re: extending a core class cleanly

    On Monday, July 05, 2010 06:16:10 pm Fearless Fool wrote:
    > David Masover wrote:
    > > What kind of surprises?

    >
    > Coming from conventional programming environments, one might be
    > surprised when a core class suddenly acquires new methods -- e.g.
    > Rails/ActiveSupport's extensions to String -- so I wanted to make sure I
    > was packaging such extensions properly.


    Libraries like that do tend to isolate the syntactic sugar from the utility
    code. I think the idea is that you might do this instead:

    module MathStuff
    def mean(array)
    ...
    end
    end

    And then, in another file:

    class Array
    def mean
    MathStuff.mean self
    end
    end

    This might be overkill, but the idea here is: If you just require
    activesupport, you get all the extensions to string. If you require
    active_support/inflector, you get all the string manipulation methods, without
    ever having to extend a string -- all your strings stay pure.

    I actually don't see a problem with extending String, and I don't see what the
    conventional argument is. The _only_ potential problem is namespace conflicts
    -- if two different libraries define String#pluralize, and it means a
    different thing to each library, you've got a problem.

    > > Then, probably, provide a file people can 'require' that'll do the
    > > inclusion for them, maybe even make that the default.

    >
    > ... or if I was feeling adventuresome, perhaps an autoload?


    That only automatically works if you then extend/include it at some point. So,
    for example, it might make sense like this:

    autoload :MathStuff, 'whatever'
    ...
    [].extend MathStuff



    It would be kind of wasteful here:



    autoload :MathStuff, 'whatever'
    class Array
    # always loads right now
    include MathStuff
    end



    And it'd be completely useless otherwise.
    David Masover, Jul 7, 2010
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    4
    Views:
    299
    James Kanze
    May 23, 2007
  2. Catherine Heathcote

    Couple of noobish question

    Catherine Heathcote, Feb 4, 2009, in forum: Python
    Replies:
    11
    Views:
    413
    Tim Rowe
    Feb 5, 2009
  3. Dirk Meijer

    very noobish eruby question

    Dirk Meijer, Sep 7, 2005, in forum: Ruby
    Replies:
    0
    Views:
    115
    Dirk Meijer
    Sep 7, 2005
  4. Paco Paco
    Replies:
    13
    Views:
    194
    Tomasz Wegrzanowski
    Nov 27, 2006
  5. Guest
    Replies:
    13
    Views:
    310
    Shai Rosenfeld
    Jul 10, 2007
Loading...

Share This Page