On 3/13/06, ara.t.howard / noaa.gov <ara.t.howard / noaa.gov> wrote:

> i disagree strongly here.  check out these two snippets that i am working on
> as we speak:
>
>      def will_process_holding *a, &b
>        raise NotImplementedError
>      end
>      def process_holding *a, &b
>        raise NotImplementedError
>      end
>      def will_process_incoming(*a, &b)
>        raise NotImplementedError
>      end
>      def process_incoming(*a, &b)
>        raise NotImplementedError
>      end
>
> vs
>
>      abstract_method "will_process_holding"
>      abstract_method "process_holding"
>      abstract_method "will_process_incoming"
>      abstract_method "process_incoming"

The question is, why use either of those methods?  Why not just note
in your documentation that those four functions must be defined?  The
user is going to have to read the documentation anyway, in order to
understand what those four abstract methods are for.  A
"NoMethodError" conveys the same information as a
"NotImplementedError", in this case.

And this is arguably an abuse of NotImplementedError.  The usual
semantics of NotImplementedError are to indicate that the method is
either a) not yet implemented by a library; or b) is not supported on
the current platform.  Whereas in the example above it is being used
to indicate that a precondition was not met.

I see that you're trying to make your code more declarative and more
explicit about it's semantics.  But seeing 'abstract_method
:will_process_holding' doesn't tell me anything more than getting a
"NoMethodError: will_process_holding".  Both of them just tell me I'm
going to have to go look at the documentation to learn what the heck
#will_process_holding is supposed to do.

If you really want to add useful semantic information, then put in
more specific error information where the "abstract" method is
*called*.  E.g.:

    def frobnicate(duck)
        duck.quack() rescue NoMethodError raise("Ducks must quack in
order to be frobnicated.  See documentation.")
    end

If you want to get fancy and declarative and proactive about it, you
could make a #precondition method:

    def frobnicate(duck)
        precondition("Ducks must quack in order to be frobnicated"){
duck.respond_to? :quack}
    ...
    end

I believe there are some libraries out there that assist in doing this
kind of "strict duck typing".

As a bonus, you are keeping your preconditions close to the code that
actually depends on them, which reduces the chances that they will get
out of sync with your code, and give the user false information.

~Avdi