The duck's backside

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, jimm@io.com, jim.menard@gmail.com
http://www.io.com/~jimm/

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 : john.carter@tait.co.nz
New Zealand

···

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.

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.

···

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.

···

On Wednesday 28 May 2008 16:25:15 John Carter wrote:

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 : john.carter@tait.co.nz
New Zealand

···

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.