Ducktator - A Duck Type Validator

Austin Z. wrote:

On 9/19/06, Ola B. [email protected] wrote:

Additionally, if it’s an external validation suite – I haven’t looked
at the project – it isn’t really checking against live code.

Why couldn’t it check against live code?

I receive an argument foo,
I check foo against a validator,
I do an operation on foo, if foo is valid:

def abc(foo)
do_operation(foo) if Ducktator.valid?(validator, foo)
end

which part isn’t live code?


Ola B. (http://ola-bini.blogspot.com)
JvYAML, RbYAML, JRuby and Jatha contributor
System Developer, Karolinska Institutet (http://www.ki.se)
OLogix Consulting (http://www.ologix.com)

“Yields falsehood when quined” yields falsehood when quined.

Austin Z. wrote:

Additionally, if it’s an external validation suite – I haven’t looked
at the project – it isn’t really checking against live code.

-austin

I beg to differ, and according to the pick-axe, this interpretation is
correct. Duck-typing isn’t to trust your callers. Duck typing is to
check if something quacks like a duck and walks like a duck. Well, me
calling respond_to? checks if the object quacks and walks. What you are
describing isn’t a technique, it is the absence of a technique.


Ola B. (http://ola-bini.blogspot.com)
JvYAML, RbYAML, JRuby and Jatha contributor
System Developer, Karolinska Institutet (http://www.ki.se)
OLogix Consulting (http://www.ologix.com)

“Yields falsehood when quined” yields falsehood when quined.

By asking if object O respond_to? method M, we can impose certain
conditions, and raise an error if those condition aren’t met, rather
than raising a runtime error in the middle of the program or getting
wonky behavior. I.e., think of it as pre-emptive TDD. This isn’t
necessary for duck-typing, but it certainly seems to be an extension
of it. Do you want your object, O, to act like a file? Then you better
make sure that it respond_to? read() and write()!

Regards.
Jordan

On 9/20/06, Ola B. [email protected] wrote:

Now, I sometimes use #respond_to? – but it isn’t duck typing.
describing isn’t a technique, it is the absence of a technique.

The problem with #respond_to? is that it only tells you that a method
is supported. It doesn’t tell you whether the method takes arguments
that quack like the ones you want to pass, or that the return value
behaves what you like. In other words, you’ll have to trust that the
method behaves how you’d like to.

Imagine:

def make_peace(world)
world.create_piece if world.respond_to? :create_piece
end

class World
def make_piece(means = War)
means.use! self
end
end

Now make_peace (called with a World) will actually start a war. See
the problem? #respond_to? will only tell you that a method is
supported, but not that it behaves how you’d like (not that static
typing guarantees this either). It saves you from NoMethodError, but
not any other error.

Duck typing is trusting other objects to do the right thing, and
#respond_to? isn’t all that useful in that regard.

On 20.09.2006 16:37, Ola B. wrote:

Now, I sometimes use #respond_to? – but it isn’t duck typing.

I beg to differ, and according to the pick-axe, this interpretation is
correct. Duck-typing isn’t to trust your callers. Duck typing is to
check if something quacks like a duck and walks like a duck.

Definitively not. What do you do if that method returns false?
Basically you can only raise an exception. Of course, you can use a
different exception type and message but that’s it.

If you need respond_to? to choose a different path of execution because
a method needs to cope with different types that’s ok but it’s not duck
typing.

Regards

robert

On Sep 20, 2006, at 10:07 AM, Simen E. wrote:

The problem with #respond_to? is that it only tells you that a method
is supported.

It doesn’t even reliably tell you that:

class Mystery
def method_missing(meth, *args, &block)
if meth == :supported
true
else
?> super

end

end
end
=> nil

m = Mystery.new
=> #Mystery:0x32b8e0

m.supported
=> true

m.respond_to? :supported
=> false

James Edward G. II

On 9/20/06, Ola B. [email protected] wrote:

Additionally, if it’s an external validation suite – I haven’t looked
at the project – it isn’t really checking against live code.
I beg to differ, and according to the pick-axe, this interpretation is
correct. Duck-typing isn’t to trust your callers. Duck typing is to
check if something quacks like a duck and walks like a duck. Well, me
calling respond_to? checks if the object quacks and walks. What you are
describing isn’t a technique, it is the absence of a technique.

You’re incorrect here. I do not need to check if someone responds to
#<< if I want to call #<< on an object. I simply call #<<. I then
document that I’m calling #<< on a received object and that if you
don’t implement #<< in a sensible way, your code is going to break.

I trust my callers not to be stupid and send me an object that doesn’t
implement #<< in this case. Checking for #<< would be the height of
stupidity.

Duck typing is treating an object like the type of object you need it
to be. It’s a technique, not an absence. It’s learning to let go of
the false sense of security that static typing gives people (and it
is a false sense of security). In my early days with Ruby, I
implemented #method_missing on a few things but didn’t implement
#respond_to? on those same things. My code would have probably worked
with my style of duck typing, but would definitely not pass
validation.

Method presence validation is really useful during testing, but is of
limited use outside of it. (However, I have used it for other things
to add methods dynamically to classes, too.)

-austin

Of course, respond_to? doesn’t tell you about what the method does. But
it isn’t meant to. To check an object’s signature is not the same thing
as to check that it does what you want it to do. A file object that
responds to write() may output a file in the current working directory;
but a mock object which is acting like a file object may just write
to screen, or may not write anything at all. But at least you can tell
that it acts like a file object so far as its signature is concerned
(viz., duck typing).

Regards,
Jordan

On 9/20/06, Ola B. [email protected] wrote:

Why couldn’t it check against live code?

I snipped context, but I explicitly said “if it’s an external
validation suite.” Based on your example (also snipped), it’s not an
external validation suite.

I doubt that I’d use it, but I think that this may have some value. It
is not, however, part of what I consider to be duck typing. It’s
contract enforcement.

-austin

On 9/20/06, MonkeeSage [email protected] wrote:

By asking if object O respond_to? method M, we can impose certain
conditions, and raise an error if those condition aren’t met, rather
than raising a runtime error in the middle of the program or getting
wonky behavior. I.e., think of it as pre-emptive TDD. This isn’t
necessary for duck-typing, but it certainly seems to be an extension
of it. Do you want your object, O, to act like a file? Then you better
make sure that it respond_to? read() and write()!

No, you better not. If I use #<<, then I can “write” to a String, a
StringIO, an IO (socket, file, etc.), or an Array. Checking for #write
limits you to (mostly) actual IO objects. Reading is a little harder,
but I tend not to bother checking for that – I generally just call
#read and tell people in the API that #read will be called at some point
and it better return something useful.

It isn’t preemptive TDD to do validation, and in Ruby ALL errors are
runtime. Method presence validation is useful and sometimes necessary;
if you look at Text::Format, you’ll see that I do both method presence
and arity validation for hyphenator objects on assignment. But I don’t
pretend that this is anything but runtime checking.

-austin

Austin Z. wrote:

No, you better not. If I use #<<, then I can “write” to a String, a
StringIO, an IO (socket, file, etc.), or an Array. Checking for #write
limits you to (mostly) actual IO objects.

Then you’d be asking if the object acts like (quacks like) an
enumerable, would you not? Why wrap that in a begin…rescue clause
and only spot it after the fact when you can catch it ahead of time?
Certainly it doesn’t violate duck-typing to ask what an object acts
like (which is what imposing conditions on an object would be doing)?!

Regards,
Jordan

On 9/20/06, MonkeeSage [email protected] wrote:

like (which is what imposing conditions on an object would be doing)?!

Because with duck typing, you break exactly at the point where an
object doesn’t behave like it’s supposed to, not before or (worse)
later.

On 9/20/06, MonkeeSage [email protected] wrote:

Austin Z. wrote:

No, you better not. If I use #<<, then I can “write” to a String, a
StringIO, an IO (socket, file, etc.), or an Array. Checking for #write
limits you to (mostly) actual IO objects.
Then you’d be asking if the object acts like (quacks like) an
enumerable, would you not? Why wrap that in a begin…rescue clause
and only spot it after the fact when you can catch it ahead of time?
Certainly it doesn’t violate duck-typing to ask what an object acts
like (which is what imposing conditions on an object would be doing)?!

As Simen E. says, preconditions or exception handling can
change the location of the error reported so that you’re not sure
whether you’re dealing with the error at the right location or not.
There are good reasons for having both, but they’re additional
techniques that can be used.

But I didn’t say anything about wrapping #<< calls in a
begin/rescue/end clause. I didn’t ask whether the object acts like an
enumerable (because, in fact, I don’t care if it’s enumerable or not).
I just told it #<< with some arguments. If it doesn’t actually
understand #<<, it’s going to break – and that means someone gave me
an object that doesn’t work like it is supposed to, or I transformed
the object in a negative way.

When one is duck typing, one is applying a concept, not formalizing an
interface. Ducktator is many things, but it isn’t an “implementation
of duck-typing”. By definition, there can be no implementation of a
duck type validator – it’s not something that is able to be
statically defined at any point.

-austin

Austin Z. wrote:

On 9/20/06, Ola B. [email protected] wrote:

The problem with “just use it”, is that you will have no control over
error handling in this case.

This is demonstrably untrue. Duck typing is not about validation. It’s
about trusting your callers to do the right thing – and then doing
the right thing when they don’t.

To extend Austin’s point a little: In ruby, it really has to be this
way. What if an object responds to a message by way of method_missing?
There’s no easy way to validate that.

On 9/20/06, MonkeeSage [email protected] wrote:

Of course, respond_to? doesn’t tell you about what the method does. But
it isn’t meant to. To check an object’s signature is not the same thing
as to check that it does what you want it to do. A file object that
responds to write() may output a file in the current working directory;
but a mock object which is acting like a file object may just write
to screen, or may not write anything at all. But at least you can tell
that it acts like a file object so far as its signature is concerned
(viz., duck typing).

Not without doing more than #respond_to? checking. See other responses
that I’ve made.

-austin
P.S. Please don’t snip context when you’re posting a response to
someone. Just because you might be using a threaded newsreader or
mailer doesn’t mean that everyone is. A little context goes a long
way.

On 9/20/06, Joel VanderWerf [email protected] wrote:

way. What if an object responds to a message by way of method_missing?
There’s no easy way to validate that.

Of course the whole scheme really relies on the library writer to
provide very good documentation - they have to (re)discover and
document every method call they perform on every object passed to
them. Unless they fall back to the old ways and say “this method
expects a string, and all that that implies”. Which doesn’t help the
user of the library much because say they want to pass in something
else that works a little like a string, but does not exhaustively
support every method the String class supports? They have to read the
library source and discover and document every method call on the
object…

Well,

I don’t want to continue this discussion in this venue, since the
discussion isn’t really about what I meant with the project and such.
Duck typing also seems to be a very loaded (might I say religious) word
for people in the Ruby-community.

I’ve written a small blog post about the issue, but I can’t guarantee
that we will all be friends, even after you read it:

Cheers

Ola B. (http://ola-bini.blogspot.com)
JvYAML, RbYAML, JRuby and Jatha contributor
System Developer, Karolinska Institutet (http://www.ki.se)
OLogix Consulting (http://www.ologix.com)

“Yields falsehood when quined” yields falsehood when quined.

Leslie V. wrote:

To extend Austin’s point a little: In ruby, it really has to be this
support every method the String class supports? They have to read the
library source and discover and document every method call on the
object…

Maybe it has to be a String - maybe not. If not, you can use a probe

class Probe
def method_missing(*a,&b)
p [self, *a]
end
end

Kind regards

robert

On 9/20/06, Robert K. [email protected] wrote:

else that works a little like a string, but does not exhaustively
end
Ah thank-you, that’s great. But it would not work unless every
execution path within the method receiving the object was executed:

class Probe
def method_missing(*a,&b)
p [self, *a]
end
end

def testProbe(probe)
if probe>5
puts probe.destroy
else
puts probe.accept
end
end

p = Probe.new
testProbe(p)


lesliev@derik:~$ ./testProbes.rb
[#Probe:0xb7cbaa70, :>, 5]
[#Probe:0xb7cbaa70, :accept]
nil

…so is there some magic that could overcome that limitation?

On 9/20/06, Robert K. [email protected] wrote:

etc. and goes on probing until no new execution sequences are found. I
guess this can soon get messy…

Yes, it starts out as a nice idea. And while it’s doing all that
probing, who knows what side-effects it might be producing in that
alien code?!

Let me see, why not try:

format_harddrive_if_input_above_5(probe)

Whoops!

Anyway, there are still places where it helps, I’m not complaining :wink:

Les