The duck's backside

Discussion in 'Ruby' started by Tobias Weber, May 28, 2008.

  1. Tobias Weber

    Tobias Weber Guest

    Hi,
    as a newcomer I miss method overloading with named parameters and types
    (Objective-C!). Mainly because my code is riddled with ugly "case
    argument.class" and "raise unless Integer". But maybe that's the problem.

    What is the Duck way to check arguments? I have to because I'm passing
    on to a rather fragile web service.

    I only need numbers (ids) and strings in my instance variables, but
    considered making a class for each one to catch illegal data in
    initialize.

    --
    Tobias Weber
     
    Tobias Weber, May 28, 2008
    #1
    1. Advertising

  2. 2008/5/28 Tobias Weber <>:
    > as a newcomer I miss method overloading with named parameters and types
    > (Objective-C!). Mainly because my code is riddled with ugly "case
    > argument.class" and "raise unless Integer". But maybe that's the problem.


    You cannot expect to carry your Objective-C style of programming over
    to Ruby. Instead, if you want to have overloading you need to either
    use different method names (appropriate if the overloaded methods act
    differently) or you can do it with a single method (e.g. if the
    overloading was just there to adjust type - for example on version
    accepts an int and another accepts a float and internally you continue
    processing with a float).

    > What is the Duck way to check arguments? I have to because I'm passing
    > on to a rather fragile web service.


    The duck way is to not check arguments.

    > I only need numbers (ids) and strings in my instance variables, but
    > considered making a class for each one to catch illegal data in
    > initialize.


    It's difficult to answer on such a general scale. Usually, if there
    are particular requirements you have on arguments passed to methods
    you should check inside the method that receives them. Note, that for
    integers there is to_int so you can do:

    class Foo
    def doit length
    @l = length.to_int
    end
    end

    Can you give a more concrete example?

    Kind regards

    robert

    --
    use.inject do |as, often| as.you_can - without end
     
    Robert Klemme, May 28, 2008
    #2
    1. Advertising

  3. Tobias Weber

    Tobias Weber Guest

    In article
    <>,
    Robert Klemme <> wrote:

    > differently) or you can do it with a single method (e.g. if the
    > overloading was just there to adjust type - for example on version


    And if the types are rather different ducks, would you "case type"?

    > are particular requirements you have on arguments passed to methods
    > you should check inside the method that receives them. Note, that for


    Yes, but it's uuugly ;)

    > def doit length
    > @l = length.to_int
    > end


    Nice, I had forgotten about that one, and to_i won't do as even nil
    answers to it.

    > Can you give a more concrete example?


    def doit(a, b, c = nil)
    begin
    if c.nil? and b > 0
    c = "ignore"
    elsif b.nil? and c > 0
    uaid = "ignore"
    else
    raise
    end
    rescue RuntimeError, NoMethodError
    raise ArgumentError, "need exactly one number"
    end
    @http.post("first=#{a}&second=#{b}&third=#{c}")
    end

    --
    Tobias Weber
     
    Tobias Weber, May 28, 2008
    #3
  4. On 28 May 2008, at 11:01, Tobias Weber wrote:
    > def doit(a, b, c = nil)
    > begin
    > if c.nil? and b > 0
    > c = "ignore"
    > elsif b.nil? and c > 0
    > uaid = "ignore"
    > else
    > raise
    > end
    > rescue RuntimeError, NoMethodError
    > raise ArgumentError, "need exactly one number"
    > end
    > @http.post("first=#{a}&second=#{b}&third=#{c}")
    > end



    This is off the top of my head so the usual caveats apply:

    def doit a, b, c = nil
    begin
    case
    when b && c
    raise
    when b && b.respond_to?:)to_int)
    uaid = "ignore" if b.to_int > 0
    when c && c.respond_to?:)to_int)
    c = "ignore" if c.to_int > 0
    else
    raise
    end
    rescue
    raise ArgumentError, "need exactly one number"
    end
    @post("first=#{a}&second=#{b.to_int}&third=#{c.to_int}")
    end

    This makes the exclusion of b and c more explicit, which will make it
    much easier for a maintainer to understand what's going on. I've also
    made integer conversion explicit and focused the filtering logic on
    making sure it's appropriate as that's what you're really trying to
    achieve, however this kind of belt-and-braces approach suggests that
    you're tackling the problem from the wrong angle and should look at
    the data-flow elsewhere in your code.

    One possibility would be to rejig the whole thing as follows:

    def doit a, v, param = :second
    raise ArgumentError, "needs a numeric value" unless v.respond_to?
    :)to_int)
    v = "ignore" if (param == :third) && v > 0
    @post("first=#{a}&second=#{v if param == :second}&third=#{v if param
    == :third}")
    end

    and then evolve it from there, although it's also pretty damn ugly.

    Where this is called you could write something along the lines of:

    if b && c then
    raise ArgumentError, "needs exactly one additional argument"
    else
    doit a, (b || c), (b.nil? ? :third : :second)
    end

    which could be further encapsulated as:

    doit a, b, c = nil
    raise ArgumentError, "needs exactly one additional argument" if b && c
    v = b || c
    raise ArgumentError, "need a numeric parameter" unless v.respond_to?
    :)to_int)
    param = b.nil? ? :third : :second
    v = "ignore" if (param == :third) && v > 0
    @post("first=#{a}&second=#{v if param == :second}&third=#{v if param
    == :third}")
    end


    Ellie

    Eleanor McHugh
    Games With Brains
    http://slides.games-with-brains.net
    ----
    raise ArgumentError unless @reality.responds_to? :reason
     
    Eleanor McHugh, May 28, 2008
    #4
  5. Tobias Weber

    Robert Dober Guest

    On Wed, May 28, 2008 at 9:45 AM, Tobias Weber <> wrote:
    >

    The first idea would be to do something like the following

    module MyChecker
    MyConstraintError = Class::new RuntimeError

    def make_ivar name, value, *allowedClasses
    raise MyConstraintError, "...." unless
    allowedClasses.any?{ |klass| klass === value }
    instance_variable_set name, value
    end
    end

    class MyClass
    include MyChecker
    def initialize int_val, str_val, val
    make_ivar :mad:int, int_val, Integer
    make_ivar :mad:str, str_val, String
    make_ivar :mad:var, val, Integer, String
    ....

    HTH
    Robert


    --
    http://ruby-smalltalk.blogspot.com/

    ---
    Whereof one cannot speak, thereof one must be silent.
    Ludwig Wittgenstein
     
    Robert Dober, May 28, 2008
    #5
  6. Tobias Weber

    Tobias Weber Guest

    In article
    <>,
    Eleanor McHugh <> wrote:

    > > uaid = "ignore"


    Ups, shoulda been c =

    > @post("first=#{a}&second=#{b.to_int}&third=#{c.to_int}")


    Won't work as the web service actually expects either a number or the
    string "ignored"

    > This makes the exclusion of b and c more explicit, which will make it


    First I actually tried exclusive or, but that operates bit-wise on
    Integer so the results were strange.

    I've always been very comfortable with perl's boolean logic, so I have
    to relearn some tricks.

    (in Ruby 0 and "" are true)

    > achieve, however this kind of belt-and-braces approach suggests that
    > you're tackling the problem from the wrong angle and should look at
    > the data-flow elsewhere in your code.


    So that doit() is only passed legal values? That should be the case, but
    I want to make extra sure not to give that foreign web service something
    it can't swallow.

    > def doit a, v, param = :second
    > raise ArgumentError, "needs a numeric value" unless v.respond_to?
    > :)to_int)
    > v = "ignore" if (param == :third) && v > 0


    That is essentially overloading doit() for first or second parameter,
    and a good idea! So far I just wrapped the http calls in convenience
    methods one by one.

    --
    Tobias Weber
     
    Tobias Weber, May 28, 2008
    #6
  7. Tobias Weber

    Guest

    On 28 Mai, 15:14, Tobias Weber <> wrote:
    > In article
    > <>,
    > Eleanor McHugh <> wrote:
    >
    > > > uaid = "ignore"

    >
    > Ups, shoulda been c =
    >
    > > @post("first=#{a}&second=#{b.to_int}&third=#{c.to_int}")

    >
    > Won't work as the web service actually expects either a number or the
    > string "ignored"


    You can do

    @post("first=#{a}&second=#{b.to_int rescue 'ignored'}&third=#{c.to_int
    rescue 'ignored'}")

    Of course, defining a method for this would be better. How about:

    def intify(x)
    x.to_int rescue "ignored"
    end

    def doit(a, b, c = nil)
    (b, c = intify(b), intify(c)).select {|x| Numeric === x}.size != 1
    and
    raise ArgumentError, "need exactly one number"
    @http.post("first=#{a}&second=#{b}&third=#{c}")
    end

    Cheers

    robert
     
    , May 28, 2008
    #7
  8. On 28 May 2008, at 14:15, Tobias Weber wrote:

    > In article
    > <>,
    > Eleanor McHugh <> wrote:
    >
    >>> uaid = "ignore"

    >
    > Ups, shoulda been c =


    Which makes things simpler ;)

    doit a, b, c = nil
    raise ArgumentError, "needs exactly one additional argument" if b && c
    v = b || c
    raise ArgumentError, "need a numeric parameter" unless v.respond_to?
    :)to_int)
    @post "first=#{a}&" + (b.nil? ? "second=ignored&third=#{v}" :
    "second=#{v}&third=ignored")
    end

    > So that doit() is only passed legal values? That should be the case,
    > but
    > I want to make extra sure not to give that foreign web service
    > something
    > it can't swallow.
    >
    >> def doit a, v, param = :second
    >> raise ArgumentError, "needs a numeric value" unless v.respond_to?
    >> :)to_int)
    >> v = "ignore" if (param == :third) && v > 0

    >
    > That is essentially overloading doit() for first or second parameter,
    > and a good idea! So far I just wrapped the http calls in convenience
    > methods one by one.


    I'm not suggesting it's a good idea...


    Ellie

    Eleanor McHugh
    Games With Brains
    http://slides.games-with-brains.net
    ----
    raise ArgumentError unless @reality.responds_to? :reason
     
    Eleanor McHugh, May 28, 2008
    #8
  9. Tobias Weber

    Mark Wilden Guest

    On May 28, 2008, at 8:24 AM, Eleanor McHugh wrote:

    > doit a, b, c = nil
    > raise ArgumentError, "needs exactly one additional argument" if b
    > && c
    > v = b || c
    > raise ArgumentError, "need a numeric parameter" unless
    > v.respond_to?:)to_int)
    > @post "first=#{a}&" + (b.nil? ? "second=ignored&third=#{v}" :
    > "second=#{v}&third=ignored")
    > end


    This looks like a great solution!

    But if you want to check for numeric parameters, would it be better
    just to use Numeric === v ? I know this isn't the ducky way, but it
    certainly seems more "intentional."

    ///ark
     
    Mark Wilden, May 28, 2008
    #9
  10. Tobias Weber

    Jim Menard Guest

    Tobias,

    > as a newcomer I miss method overloading with named parameters and types
    > (Objective-C!). Mainly because my code is riddled with ugly "case
    > argument.class" and "raise unless Integer". But maybe that's the problem.
    >
    > What is the Duck way to check arguments? I have to because I'm passing
    > on to a rather fragile web service.


    Don't bother. Use your arguments, assuming they are the correct type.
    If they are not, then a runtime exception will be thrown. Unless the
    input to this function is coming from an untrusted source, the inputs
    will always be the correct type. If they are not, then something is
    seriously wrong.

    Jim

    >
    > I only need numbers (ids) and strings in my instance variables, but
    > considered making a class for each one to catch illegal data in
    > initialize.
    >
    > --
    > Tobias Weber
    >
    >




    --
    Jim Menard, ,
    http://www.io.com/~jimm/
     
    Jim Menard, May 28, 2008
    #10
  11. On 28 May 2008, at 18:57, Mark Wilden wrote:
    > On May 28, 2008, at 8:24 AM, Eleanor McHugh wrote:
    >> doit a, b, c = nil
    >> raise ArgumentError, "needs exactly one additional argument" if b
    >> && c
    >> v = b || c
    >> raise ArgumentError, "need a numeric parameter" unless
    >> v.respond_to?:)to_int)
    >> @post "first=#{a}&" + (b.nil? ? "second=ignored&third=#{v}" :
    >> "second=#{v}&third=ignored")
    >> end

    >
    > This looks like a great solution!
    >
    > But if you want to check for numeric parameters, would it be better
    > just to use Numeric === v ? I know this isn't the ducky way, but it
    > certainly seems more "intentional."


    That'd be marginally more readable but to my mind it's overly
    cautious: what's the point of using Ruby if you're still going to
    follow static typing conventions?

    Ellie

    Eleanor McHugh
    Games With Brains
    http://slides.games-with-brains.net
    ----
    raise ArgumentError unless @reality.responds_to? :reason
     
    Eleanor McHugh, May 28, 2008
    #11
  12. Tobias Weber

    Mark Wilden Guest

    On May 28, 2008, at 11:45 AM, Eleanor McHugh wrote:

    >> But if you want to check for numeric parameters, would it be better
    >> just to use Numeric === v ? I know this isn't the ducky way, but it
    >> certainly seems more "intentional."

    >
    > That'd be marginally more readable but to my mind it's overly
    > cautious: what's the point of using Ruby if you're still going to
    > follow static typing conventions?


    The only point of using Ruby (or any other tool in the universe) is to
    get a job done. :) I would never use a Ruby feature just because Ruby
    makes it possible.

    Numeric === v is not a static typing convention (see http://en.wikipedia.org/wiki/Type_system)
    It's just OOP. It says in computer-understandable terms exactly what
    you expressed in English: "v is numeric."

    I don't even know what the ability to respond to :to_int means. Does
    that mean the entity is numeric? Does a numeric string qualify? Does a
    floating point number qualify? Could Array support this method? Does
    every class that responds to to_int do semantically equivalent things?
    (think Cowboy#draw and Artist#draw). The only way to tell is to test
    it or look it up. This doesn't make it "marginally" less readable than
    Numeric === v, IMO.

    Different strokes, of course...

    ///ark
     
    Mark Wilden, May 28, 2008
    #12
  13. On Wednesday 28 May 2008 15:09:23 Mark Wilden wrote:
    > On May 28, 2008, at 11:45 AM, Eleanor McHugh wrote:


    > > That'd be marginally more readable but to my mind it's overly
    > > cautious: what's the point of using Ruby if you're still going to
    > > follow static typing conventions?

    >
    > The only point of using Ruby (or any other tool in the universe) is to
    > get a job done. :) I would never use a Ruby feature just because Ruby
    > makes it possible.


    Put it this way: What's the point of using, say, Erlang, if you never use its
    concurrency features?

    Granted, Ruby has a bit more going for it, but a lot of the joy of using Ruby
    is how much less work you end up having to do when you don't have to think
    about types too much.

    > . It's just OOP. It says in computer-understandable terms exactly what
    > you expressed in English: "v is numeric."


    No, it says "v is of class Numeric." It's very explicit, and makes the
    assumption that anything which acts like a number will eventually inherit
    from Numeric.

    > I don't even know what the ability to respond to :to_int means.


    I think it would be :to_i, actually... And I'd probably just call foo.to_i,
    rather than testing for the existence of to_i.

    Another possibility is: v.integer?

    > Does
    > that mean the entity is numeric?


    In a sense.

    > Does a numeric string qualify?


    Yes, it'll be parsed.

    > Does a
    > floating point number qualify?


    Yes, it'll be rounded down.

    > Could Array support this method?


    Anything could. Open classes mean you could add one to Array. But out of the
    box, it doesn't appear to.

    > Does
    > every class that responds to to_int do semantically equivalent things?


    Probably -- to_i is very much baked into the language, along with to_s and
    friends.

    Remember the above -- I could actually completely redefine Array, or Numeric,
    etc. So even your assumption that "Numeric === foo" tests for Numeric is
    really only based on convention -- you're assuming that no one, anywhere in
    your code, is doing stuff like this:

    Numeric = nil

    > (think Cowboy#draw and Artist#draw).


    Yes, that is a downside of duck typing, as currently implemented, but doesn't
    really apply to :to_i.

    Also, context matters. If Cowboy and Artist are both in a GUI widget library,
    that Cowboy is asking for trouble. And it's worth asking how the Cowboy came
    to be in an art studio, or how the Artist came to be in a saloon -- there are
    likely going to be more elegant solutions than

    if Cowboy === bill
    ...
     
    David Masover, May 28, 2008
    #13
  14. Tobias Weber

    John Carter Guest

    On Thu, 29 May 2008, Jim Menard wrote:

    >> What is the Duck way to check arguments? I have to because I'm passing
    >> on to a rather fragile web service.

    >
    > Don't bother. Use your arguments, assuming they are the correct type.
    > If they are not, then a runtime exception will be thrown.


    Yes and No.

    If the bug is on the backtrace, this works fine. An exception is
    thrown, the backtrace is printed, and you inspect each one and you
    find the wrong code, and fix it.

    End of story.

    If however you are doing, as is quite often done, constructing an
    object, passing in some of the instance variables as parameters to the
    constructor...

    The use that triggers the exception, is quite likely _not_ to have the
    buggy line on the backtrace. The buggy line was on the call graph
    heading towards the constructor, not the use of that instance.

    The duck's backside is not watertight, the duck sinks.

    Hence my static_type_check set of utilities...

    So in...

    class MyObject

    def initialize( _a, _b, _c)
    @a = _a.quacks_like( :qwaak, :waddle, :swim)
    @b = _b.static_type_check String
    @c = _c.polymorphic_type_check MyBaseClass
    end

    # Without the checks above, the exceptions would be thrown here, with the buggyline nowhere on the backtrace!
    def my_use
    @a.qwaak( @b) + @c
    end
    end

    # Abstract base class for all the type check exceptions
    class TypeCheckException < Exception
    end

    # This exception is thrown in event of a method being invoked with an
    # object of the wrong duck type.
    class DuckTypingException < TypeCheckException
    end

    # This exception is thrown in event of a method being invoked with a
    # parameter of of the wrong static type.
    class StaticTypeException < TypeCheckException
    end

    # This exception is thrown in event of a method being invoked with a
    # parameter of of the wrong polymorphic type.
    class PolymorphicTypeException < TypeCheckException
    end

    class Object

    # Raise a DuckTypingException unless the object responds to all symbol.
    def quacks_like( *symbols)
    symbols.each do |symbol|
    raise DuckTypingException, "Duck typing error, expected this object to respond to :#{symbol}, but found class #{self.class}\n\t\t#{symbol.inspect}" unless
    respond_to? symbol
    end
    end

    def static_type_check_boolean
    raise StaticTypeException, "Static type check error, expected object to be a boolean, found '#{self.class}'
    \t\t#{self.inspect}" unless
    (self.class == TrueClass) ||
    (self.class == FalseClass) ||
    (self.class == NilClass)
    self
    end


    def static_type_check( klass)
    raise StaticTypeException, "Static type check error, expected object to be exactly class '#{klass}', found '#{self.class}'\n\t\t#{self.inspect}" unless
    self.class == klass
    self
    end

    def static_type_check_each( klass)
    each do |object|
    object.static_type_check klass
    end
    self
    end

    def polymorphic_type_check_each( klass)
    each do |object|
    object.polymorphic_type_check klass
    end
    self
    end

    def polymorphic_type_check( klass)
    # @@static_caller[caller[1]]+=1
    raise PolymorphicTypeException, "Polymorphic type check error, expected object to be a kind of '#{klass}', found '#{self.class}'\n\t\t#{self.inspect}" unless
    self.kind_of? klass
    self
    end

    end



    John Carter Phone : (64)(3) 358 6639
    Tait Electronics Fax : (64)(3) 359 4632
    PO Box 1645 Christchurch Email :
    New Zealand
     
    John Carter, May 28, 2008
    #14
  15. On Wednesday 28 May 2008 16:25:15 John Carter wrote:

    > class MyObject
    >
    > def initialize( _a, _b, _c)
    > @a = _a.quacks_like( :qwaak, :waddle, :swim)
    > @b = _b.static_type_check String
    > @c = _c.polymorphic_type_check MyBaseClass
    > end
    >
    > # Without the checks above, the exceptions would be thrown here, with the

    buggyline nowhere on the backtrace!
    > def my_use
    > @a.qwaak( @b) + @c
    > end
    > end


    I'm fine with that, because 99% of the time, I'll never hit that exception.
    When I do, it's usually possible to deal with it -- in this case, you would
    find where @a is set, and add a check there.

    I think you're arguing to add that check first. I would call that premature
    strictness -- but if you have to do it, I like your way.

    Oh, also: Most of the time, I use setters/getters (attr_accessor), even when
    something's mostly going to be internal. That simplifies this process -- I
    know there was a problem with a, so I can override a's setter with something
    that performs the same check. But that's also probably overkill -- if a class
    can't fit on a page, it's probably too big and should be broken down.
     
    David Masover, May 28, 2008
    #15
  16. On Wednesday 28 May 2008 16:25:15 John Carter wrote:
    > # Raise a DuckTypingException unless the object responds to all symbol.
    > def quacks_like( *symbols)
    > symbols.each do |symbol|
    > raise DuckTypingException, "Duck typing error, expected this

    object to respond to :#{symbol}, but found class
    #{self.class}\n\t\t#{symbol.inspect}" unless
    > respond_to? symbol
    > end
    > end


    Just occurred to me that this does have one flaw: It cannot check for what
    happens when method_missing is hit. I doubt that's much of a problem in
    practice, though.
     
    David Masover, May 28, 2008
    #16
  17. Tobias Weber

    Mark Wilden Guest

    On May 28, 2008, at 1:44 PM, David Masover wrote:

    >> The only point of using Ruby (or any other tool in the universe) is
    >> to
    >> get a job done. :) I would never use a Ruby feature just because Ruby
    >> makes it possible.

    >
    > Put it this way: What's the point of using, say, Erlang, if you
    > never use its
    > concurrency features?


    All I can do is repeat what I said: the ONLY point to ANYthing (in
    programming) is as a means to accomplish some purpose. If using Erlang
    accomplishes a given purpose best, then it should be used, no matter
    what its concurrency capabilities. One reason might simply be that a
    given programmer is more comfortable in that language than another,
    and can therefore accomplish the task faster.

    > Granted, Ruby has a bit more going for it, but a lot of the joy of
    > using Ruby
    > is how much less work you end up having to do when you don't have to
    > think
    > about types too much.


    I think you mean "classes," not "types." At any rate, I don't see how
    Numeric === v involves more work than the alternative, so I don't
    think that criterion applies here.

    > No, it says "v is of class Numeric." It's very explicit, and makes the
    > assumption that anything which acts like a number will eventually
    > inherit
    > from Numeric.


    Well, the desired goal is in fact to recognize objects that are
    numeric. One of the purposes of classes in OOP is to categorize things.

    >> I don't even know what the ability to respond to :to_int means.

    >
    > I think it would be :to_i, actually... And I'd probably just call
    > foo.to_i,
    > rather than testing for the existence of to_i.


    :to_i is actually rather different, as it will convert a string (even
    a non-numeric string like "2a"). That's an example of having to know
    the implementation of the method to determine whether testing
    responds_to? makes sense. For all I know, :to_i may in fact be the
    desired method, but Elizabeth used :to_int, and I assume it was for a
    reason.

    > Remember the above -- I could actually completely redefine Array, or
    > Numeric,
    > etc. So even your assumption that "Numeric === foo" tests for
    > Numeric is
    > really only based on convention -- you're assuming that no one,
    > anywhere in
    > your code, is doing stuff like this:
    >
    > Numeric = nil


    That's not merely being unconventional--that's insanity. :) Anyway, it
    applies even more so to methods, which aren't in the global namespace.

    >> (think Cowboy#draw and Artist#draw).

    >
    > Yes, that is a downside of duck typing, as currently implemented,
    > but doesn't
    > really apply to :to_i.
    >
    > Also, context matters. If Cowboy and Artist are both in a GUI widget
    > library,
    > that Cowboy is asking for trouble.


    That's true, but it wasn't my point. The question is whether all
    methods with the same name in all active classes should be
    semantically equivalent. I think that's a rather larger assumption
    than that Numeric means "numeric."

    ///ark
     
    Mark Wilden, May 28, 2008
    #17
  18. Tobias Weber

    John Carter Guest

    On Thu, 29 May 2008, David Masover wrote:

    > I'm fine with that, because 99% of the time, I'll never hit that exception.
    > When I do, it's usually possible to deal with it -- in this case, you would
    > find where @a is set, and add a check there.
    >
    > I think you're arguing to add that check first. I would call that premature
    > strictness -- but if you have to do it, I like your way.


    Depends on what I'm doing. If I'm just doing bog standard coding, I'll
    probably skip the checks.

    If I'm doing something wildly experimental, or ripping up and
    restructuring a large chunk of code, I'll probably splash checks
    around with a heavy hand.

    Gives me a quick heads up that I'm being stupid. Forces me to think a
    little, "what do I really want coming it here? I Dunno really, but it
    must quack like this. "

    Run the unit test, ooh looky, I'm getting something that doesn't quack
    like that. No, no surprise now I think about it, but it holds, has, or
    makes a thing that quacks like that. A quick fix in the editor and off
    I go again.




    John Carter Phone : (64)(3) 358 6639
    Tait Electronics Fax : (64)(3) 359 4632
    PO Box 1645 Christchurch Email :
    New Zealand
     
    John Carter, May 29, 2008
    #18
  19. On Wednesday 28 May 2008 17:23:30 Mark Wilden wrote:
    > On May 28, 2008, at 1:44 PM, David Masover wrote:
    >
    > > Put it this way: What's the point of using, say, Erlang, if you
    > > never use its
    > > concurrency features?

    >
    > All I can do is repeat what I said: the ONLY point to ANYthing (in
    > programming) is as a means to accomplish some purpose. If using Erlang
    > accomplishes a given purpose best, then it should be used, no matter
    > what its concurrency capabilities. One reason might simply be that a
    > given programmer is more comfortable in that language than another,
    > and can therefore accomplish the task faster.


    Even if the problem doesn't require concurrency, the main reason for choosing
    Erlang in the first place is its threading model. If you don't like Erlang's
    threading, chances are, the rest of it is done better in other languages.

    I'm not saying that we don't want you if you won't do Ruby duck typing. Just
    saying that I consider duck typing to be a major draw to Ruby in the first
    place.

    I kind of feel like you're doing the equivalent of this:

    i = 0
    while(i < some_array.length)
    do_something_with(some_array)
    i += 1
    end

    Yes, Ruby can do that, but I think most of us agree that a major appeal of
    Ruby is being able to do this instead:

    some_array.each { |item|
    do_something_with item
    }

    > > No, it says "v is of class Numeric." It's very explicit, and makes the
    > > assumption that anything which acts like a number will eventually
    > > inherit
    > > from Numeric.

    >
    > Well, the desired goal is in fact to recognize objects that are
    > numeric. One of the purposes of classes in OOP is to categorize things.


    Given single inheritance, you're not going to force everything into the exact
    category it belongs. I know I implemented a class to represent DNS serial
    numbers and their associated math. It didn't inherit from Numeric, but it did
    have to_i.

    Maybe that was bad design on my part, but the point of duck typing is that we
    don't need to care if it calls itself "Numeric". Instead, we care that it
    acts like a Numeric -- it responds_to +, -, and probably to_i and integer?

    You've probably heard all this before, of course.

    > > Remember the above -- I could actually completely redefine Array, or
    > > Numeric,
    > > etc. So even your assumption that "Numeric === foo" tests for
    > > Numeric is
    > > really only based on convention -- you're assuming that no one,
    > > anywhere in
    > > your code, is doing stuff like this:
    > >
    > > Numeric = nil

    >
    > That's not merely being unconventional--that's insanity. :) Anyway, it
    > applies even more so to methods, which aren't in the global namespace.


    Alright, without altering the global namespace, and with very possibly a good
    excuse, I could do something like this:

    class IpAddress < Integer
    def to_s
    # return '12.34.56.78'
    end
    end

    Now, that actually won't work without a bit of massaging -- the numeric
    classes don't have constructors. And there's already a built in IP address
    class, so this would be pointless.

    But I'm not sure there's any more reason to believe that something which is a
    Numeric (or claims to be) is going to give you the semantics you want, than
    to believe the same of something which supports to_i (or to_int, which is
    probably more correct).

    > >> (think Cowboy#draw and Artist#draw).

    > >
    > > Yes, that is a downside of duck typing, as currently implemented,
    > > but doesn't
    > > really apply to :to_i.
    > >
    > > Also, context matters. If Cowboy and Artist are both in a GUI widget
    > > library,
    > > that Cowboy is asking for trouble.

    >
    > That's true, but it wasn't my point. The question is whether all
    > methods with the same name in all active classes should be
    > semantically equivalent. I think that's a rather larger assumption
    > than that Numeric means "numeric."


    The assumption you're making with Numeric isn't that Numeric means "numeric",
    but that all possibly numeric values are contained in Numeric.

    I think your use case had something to do with a fragile web service, so this
    actually could make a lot of sense to you -- it might even be worth checking
    if it's an Integer.

    But in the general case, I like that much of my code is actually abstract
    enough to swap something of an entirely different type (or class) in and have
    it do something useful. Recently, I actually wrote an entire class for
    storing things (a two-dimensional map) without knowing what kind of object
    I'd be filling it with. I ended up filling it with Symbols.
     
    David Masover, May 29, 2008
    #19
  20. Tobias Weber

    Mark Wilden Guest

    On May 28, 2008, at 6:25 PM, David Masover wrote:

    > I think your use case had something to do with a fragile web
    > service, so this
    > actually could make a lot of sense to you -- it might even be worth
    > checking
    > if it's an Integer.


    Wasn't my use case. :)

    ///ark
     
    Mark Wilden, May 29, 2008
    #20
    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. Duck Typing

    , Sep 16, 2003, in forum: Python
    Replies:
    10
    Views:
    706
    Steve Holden
    Sep 17, 2003
  2. Replies:
    15
    Views:
    578
    Isaac Gouy
    Sep 1, 2006
  3. Replies:
    2
    Views:
    331
    Terry Reedy
    Jan 28, 2008
  4. t7dgbnak

    Black Duck Outfitters!!! RIP OFF

    t7dgbnak, Apr 2, 2008, in forum: C Programming
    Replies:
    0
    Views:
    576
    t7dgbnak
    Apr 2, 2008
  5. Paddy
    Replies:
    1
    Views:
    412
    Paddy
    May 9, 2008
Loading...

Share This Page