Duck Typing as Pattern Matching

I haven't read the entire thread so forgive me if I'm rehashing....

"ts" <decoux@moulon.inra.fr> wrote in message

> p txn.respond_to?(:create_modify)
> p txn.create_modify

Oh oh, even more Ruby experts join the chorus ? :slight_smile:

My claim is this: If you redefine #method_missing so your object handles
x.foo as the caller would expect, then you _should_ define
x.respond_to?(:foo) to return true. I do not claim that Ruby should
prohibit code that does not do this.

I wonder if method missing would be better if these two "tiers" were built in
to it. This would require dual "blocks" so I'm not sure how to notate it but:

  def method_missing(meth, *args)
  upon meth == :create_modify
    # do whatever
  upon ...
    # ...
  end

Then #respond_to? can utilize the "upon" block to determine responses
correctly.

I am interested in your thoughts on this claim.

p.s. I know it is possible to redefine #send so that x.send :foo works, but
x.foo does not. I think this a practice to be actively discouraged. But
let's leave that out for now.

Redefining #send is a very bad idea. IMHO That's one of those methods I think
should be off limits and return an error if you try to redefine it.

T.

···

On Thursday 13 January 2005 09:51 am, itsme213 wrote:

My claim is this: If you redefine #method_missing so your object handles
x.foo as the caller would expect, then you _should_ define
x.respond_to?(:foo) to return true. I do not claim that Ruby should prohibit
code that does not do this.

well, difficult, for me, to reply because you must see the underlying C++
interface (DbXml) to see why bdbxml do this.

Normally the code must be written

  p txn.manager.create_modify

i.e.

   `txn.create_modify' is *exactly* the same than `txn.manager.create_modify'

but, to give you, another example

uln% cat b.rb
#!/usr/local/bin/ruby
$LOAD_PATH.unshift("../src")
require 'bdbxml'

txn = BDB::Env.new("tmp",BDB::CREATE|BDB::INIT_TRANSACTION).manager.
   transaction
p txn.respond_to?(:create_container)
p txn.create_container("aaa")
txn.commit
uln%

uln% b.rb
true
#<BDB::XML::Container:0x2a955b2ef8>
uln%

in this case `txn.create_container' is *really* different from
`txn.manager.create_container'

Transaction#create_modify is just a convenient method.

Guy Decoux

"David A. Black" <dblack@wobblini.net> wrote in message

>> The whole point of method_missing is that it catches missing methods.
>
> Sure. And if, after catching it, you do something which the (original)
> caller would consider a valid handling of the (original) request, then

you

> should indicate the fact in your 'respond_to?'. Otoh, if your
> method_missing simply interposes some exception stuff, and perhaps then
> calls super, then you have not handled the client request. In that case

you

> did not respond_to the client request.

I don't agree that that's a necessary formula, or that nothing that
doesn't fit into it should exist.

I did not claim either. But for something to work properly with code that
does
    x.foo if (x.respond_to? :foo) # quite reasonable, you'll agree
it must follow that rule.

A good example is OpenStruct, which
takes advantage of the open-endedness of method_missing.

I would say that for each of the method o.m calls that OpenStruct handles
via method_missing (I think those are #m(), and #m=(x), for any m), it
_should_ return true o.respond_to?(:m). And without that, OpenStruct objects
will not work properly with (if x.respond_to?) code. And since Ruby does not
treat arity or arg-names as part of the method signature or selector (a
whole other discussion), there is no way for respond_to to distinguish
between x.foo and x.foo(y). Hence OpenStruct should have:
    def respond_to(x); true; end

>> You're not expected to enumerate them by name

Correct. And #respond_to? does not enumerate by name, #methods does. If I
implemented a proxy that simply forwarded all missing requests, I would do:
class Proxy
    def init(real); @real = real; end
    def method_missing(*args); @real.send *args; end
    def respond_to?(x); super || @real.respond_to(x); end
end

>> if the
>> object thinks it responds to them, then they're not missing.

Correct. So it should reply true to respond_to? for those cases.

(Of course you could just do:

   def respond_to?(m)
     true
   end

to mirror the flexibility of method_missing :slight_smile:

Which would be OK if (and only if) a client call,
    x.foo
for *any* foo, was actually handled by your method_missing to behave as that
client would expect. See proxy or openstruct example above.

No prob if this thread is wound down ... I don't mind talking to myself,
occaisonally I might even listen :slight_smile:

> My claim is this: If you redefine #method_missing so your object handles
> x.foo as the caller would expect, then you _should_ define
> x.respond_to?(:foo) to return true. I do not claim that Ruby should
> prohibit code that does not do this.

I wonder if method missing would be better if these two "tiers" were built

in

to it. This would require dual "blocks" so I'm not sure how to notate it

but:

  def method_missing(meth, *args)
  upon meth == :create_modify
    # do whatever
  upon ...
    # ...
  end

Then #respond_to? can utilize the "upon" block to determine responses
correctly.

Not sure what "upon" is. If your intent is to associated distinct
method_missing handler procs with selectors, so that Object#respond_to? can
utilize this properly, it can be done in many ways. e.g. Object could
provide a register_method_missing_handler:

class MyClass
    register_method_missing_handler :selector, { |*args| handler }
end

Then Object#respond_to? could utilize the registered selectors.

One difficulty would be generic handlers (e.g. a proxy handles anything its
real target does, OpenStruct handles any x() and x(y)), so the "associated
selectors" would need to include something like pattern matching or
wildcards. Oh, oh, ... does that mean we are back to duck typing based on
pattern matching ? :wink:

A separate, but related, issue is that I _really_ think anywhere selectors
are passed around in 2.0 they should uniformly include information about
method arity, keyword arg names, and block params (at least optionally). If
2.0 does selector namespaces, then selectors have to include namespace info
as well.
    x.respond_to?(:x) where :x is in namespace-1
    x.respond_to?(x) is :x is in namespace-2.

I think selectors

> I am interested in your thoughts on this claim.
>
> p.s. I know it is possible to redefine #send so that x.send :foo works,

but

> x.foo does not. I think this a practice to be actively discouraged. But
> let's leave that out for now.

Redefining #send is a very bad idea. IMHO That's one of those methods I

think

···

"trans. (T. Onoma)" <transami@runbox.com> wrote

should be off limits and return an error if you try to redefine it.

T.

Completing that last posting ...

A separate, but related, issue is that I _really_ think anywhere selectors
are passed around in 2.0 they should uniformly include information about
method arity, keyword arg names, and block params (at least optionally).

If

2.0 does selector namespaces, then selectors have to include namespace

info

as well.
    x.respond_to?(:x) where :x is in namespace-1
    x.respond_to?(x) is :x is in namespace-2.

Thus respond_to?(x) could:
- make x a namespace-aware selector, not just a symbol
- return (also?) a MethodInfo object with arity, key-args, block

When we register_method_missing_handlers Ruby can extract MethodInfo from
the block.

···

"itsme213" <itsme213@hotmail.com> wrote

Of course if selector is a fixed symbol you wouldn't need this as you would
just use def or define_method.

···

"itsme213" <itsme213@hotmail.com> wrote

class MyClass
    register_method_missing_handler :selector, { |*args| handler }
end

Of course :wink: And that's why I used "upon" and not just a case statement or
secondary "respond_if" method to invoke a method_missing handler.

T.

P.S. Of course the coolest of cool would be if Ruby were so reflectively
powerful as to allow for true method probing. Then Ruby could simply probe
#method_missing and determine if it responds. But of course, that's _so cool_
b/c in fact it may not even be possible.

···

On Thursday 13 January 2005 01:06 pm, itsme213 wrote:

Not sure what "upon" is. If your intent is to associated distinct
method_missing handler procs with selectors, so that Object#respond_to? can
utilize this properly, it can be done in many ways. e.g. Object could
provide a register_method_missing_handler:

class MyClass
    register_method_missing_handler :selector, { |*args| handler }
end

Then Object#respond_to? could utilize the registered selectors.

One difficulty would be generic handlers (e.g. a proxy handles anything its
real target does, OpenStruct handles any x() and x(y)), so the "associated
selectors" would need to include something like pattern matching or
wildcards. Oh, oh, ... does that mean we are back to duck typing based on
pattern matching ? :wink: