Respond to an unknown method call: Difference between revisions

From Rosetta Code
Content added Content deleted
(omission list extended)
(add scala)
Line 433: Line 433:
example.ding("dong") # prints “tried to handle unknown method ding”
example.ding("dong") # prints “tried to handle unknown method ding”
# prints “it had arguments: ["dong"]”</lang>
# prints “it had arguments: ["dong"]”</lang>

=={{header|Scala}}==
{{works with|Scala|2.9}}
As of scala 2.9, scalac must receive the -Xexperimental optional for Dynamic to receive this treatment.
<lang scala>class DynamicTest extends Dynamic
{
def foo()=println("this is foo")
def bar()=println("this is bar")
def applyDynamic(name: String)(args: Any*)={
println("tried to handle unknown method "+name)
if(!args.isEmpty)
println(" it had arguments: "+args.mkString(","))
}
}

object DynamicTest {
def main(args: Array[String]): Unit = {
val d=new DynamicTest()
d.foo()
d.bar()
d.grill()
d.ding("dong")
}
}
</lang>


=={{header|Slate}}==
=={{header|Slate}}==

Revision as of 08:59, 11 July 2011

Task
Respond to an unknown method call
You are encouraged to solve this task according to the task description, using any language you may know.

Demonstrate how to make the object respond (sensibly/usefully) to an invocation of a method on it that it does not support through its class definitions. Note that this is not the same as just invoking a defined method whose name is given dynamically; the method named at the point of invocation must not be defined.

This task is intended only for object systems that use a dynamic dispatch mechanism without static checking.

Brat

<lang brat>example = object.new

example.no_method = { meth_name, *args |

 p "#{meth_name} was called with these arguments: #{args}"

}

example.this_does_not_exist "at all" #Prints "this_does_not_exist was called with these arguments: [at all]"</lang>

C#

Works with: C sharp version 4.0

<lang csharp>using System; using System.Dynamic;

class Example : DynamicObject {

   public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
   {
       result = null;
       Console.WriteLine("This is {0}.", binder.Name);
       return true;
   }

}

class Program {

   static void Main(string[] args)
   {
       dynamic ex = new Example();
       ex.Foo();
       ex.Bar();
   }

}</lang>

Common Lisp

In Common Lisp, if a generic function is invoked on arguments for which there is no applicable specialized method, the method no-applicable-method is called with the generic function and the arguments.

<lang lisp>(defgeneric do-something (thing)

 (:documentation "Do something to thing."))

(defmethod no-applicable-method ((method (eql #'do-something)) &rest args)

 (format nil "No method for ~w on ~w." method args))

(defmethod do-something ((thing (eql 3)))

 (format nil "Do something to ~w." thing))</lang>

Evaluating<lang lisp>(list (do-something 3) (do-something 4))</lang> produces

<lang lisp>("Do something to 3."

"No method for #<STANDARD-GENERIC-FUNCTION DO-SOMETHING 214FC042> on (4).")</lang>

D

D v.2. code, from the Python version. D performs this statically. <lang d>import std.stdio: write, writeln;

struct Example {

   void foo() { writeln("this is foo"); }
   void bar() { writeln("this is bar"); }
   void opDispatch(string name, Types...)(Types args) {
       write("tried to handle unknown method ", name);
       if (Types.length) {
           write(", it had arguments: ");
           foreach (arg; args)
               write(arg, " ");
       }
       writeln();
   }

}

void main() {

   Example example;
   example.foo();        // this is foo
   example.bar();        // this is bar
   example.grill();      // tried to handle unknown method grill
   example.ding("dong"); // tried to handle unknown method ding, it had arguments: dong

}</lang>

E

In E, a message consists of a verb (arbitrary string) and arguments (sequence of arbitrary objects). It is conceptually entirely up to any given object how it dispatches incoming messages.

Practically, the object definition syntax provides a matcher clause to handle unrecognized messages. This example has the same behavior as the Python example.

<lang e>def example {

   to foo() { println("this is foo") }
   to bar() { println("this is bar") }
   match [verb, args] {
       println(`got unrecognized message $verb`)
       if (args.size() > 0) {
           println(`it had arguments: $args`)
       }
   }

}</lang>

Fancy

<lang fancy> def class CatchThemAll {

 def foo {
   "foo received" println
 }
 def bar {
   "bar received" println
 }
 def unknown_message: msg with_params: params {
   "message: " ++ msg print
   "arguments: " ++ (params join: ", ") println
 }

}

a = CatchThemAll new a foo a bar a we_can_do_it a they_can_too: "eat" and: "walk" </lang>

Fantom

In Fantom, you can call methods statically or dynamically. Static calls to methods will be checked at compile time. Dynamic method calls (indicated by an instance->method syntax) are run through a "trap" method at run time. This method looks up the given method name, and throws an exception if the method/field is not known. This exception can be caught, and processed specially:

<lang fantom> class A {

 public Void doit (Int n)
 {
   echo ("known function called on $n")
 }
 // override the 'trap' method, which catches dynamic invocations of methods
 override Obj? trap(Str name, Obj?[]? args := null)
 {
   try 
   {
     return super.trap(name, args)
   } 
   catch (UnknownSlotErr err)
   {
     echo ("In trap, you called: " + name + " with args " + args.join(","))
     return null
   }
 }

}

class Main {

 public static Void main ()
 {
   a := A()
   // note the dynamic dispatch
   a->doit (1)
   a->methodName (1, 2, 3)
 }

} </lang>

Output:

$ fan unknown-method.fan 
known function called on 1
In trap, you called: methodName with args 1,2,3

J

In J, you can define a method at invocation time, and this definition can be in z which is the parent from which all objects and classes inherit.

For example, we could define

<lang J> example=:3 :0

  doSomething_z_=: assert&0 bind 'doSomething was not implemented'
  doSomething__y 

)

doSomething_adhoc1_=: smoutput bind 'hello world' dSomethingElse_adhoc2_=: smoutput bind 'hello world'</lang>

With those definitions in a fresh session (where adhoc2 has not been given a definition for doSomething), we get the following behavior:

<lang J> example <'adhoc1' hello world

  example<'adhoc2'

|doSomething was not implemented: assert </lang>


JavaScript

There is no standard way to do this, but some implementations provide suitable traps.

Works with: Firefox

<lang javascript>var example = new Object; example.foo = function () {

 alert("this is foo");

} example.bar = function () {

 alert("this is bar");

} example.__noSuchMethod__ = function (id, args) {

 alert("tried to handle unknown method " + id);
 if (args.length != 0)
   alert("it had arguments: " + args);

}

example.foo(); // alerts "this is foo" example.bar(); // alerts "this is bar" example.grill(); // alerts "tried to handle unknown method grill" example.ding("dong"); // alerts "tried to handle unknown method ding"

                     // alerts "it had arguments: dong</lang>

Lua

This is specifically the purpose of the __index metamethod:

<lang Lua> local object={print=print} setmetatable(object,{__index=function(t,k)return function() print("You called the method",k)end end}) object.print("Hi") -->Hi object.hello() -->You called the method hello </lang>

Objective-C

-forwardInvocation: is usually used to "forward" the message on to another object to handle.

<lang objc>#include <Foundation/Foundation.h>

// The methods need to be declared somewhere @interface Dummy : NSObject { } - (void)grill; - (void)ding:(NSString *)s; @end

@interface Example : NSObject { } - (void)foo; - (void)bar; @end

@implementation Example - (void)foo {

 NSLog(@"this is foo");

} - (void)bar {

 NSLog(@"this is bar");

} - (void)forwardInvocation:(NSInvocation *)inv {

 NSLog(@"tried to handle unknown method %@", NSStringFromSelector([inv selector]));
 unsigned n = [[inv methodSignature] numberOfArguments];
 unsigned i;
 for (i = 0; i < n-2; i++) { // first two arguments are the object and selector
   id arg;
   [inv getArgument:&arg atIndex:i+2];
   NSLog(@"argument #%u: %@", i, arg);
 }

} @end

int main() {

 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
 id example = [[Example alloc] init];
 [example foo];          // prints "this is foo"
 [example bar];          // prints "this is bar"
 [example grill];        // prints "tried to handle unknown method grill"
 [example ding:@"dong"]; // prints "tried to handle unknown method ding:"
                         // prints "argument #0: dong"
 [example release];
 [pool release];
 return 0;

}</lang>

Oz

To respond to unknown method calls, classes can implement the otherwise method. As its sole argument, this method gets the received message, i.e. a record with the name of the unknown method as its label and the arguments as the record features. <lang oz>declare

 class Example
    meth init skip end

    meth foo {System.showInfo foo} end

    meth bar {System.showInfo bar} end

    meth otherwise(Msg)
       {System.showInfo "Unknown method "#{Label Msg}}
       if {Width Msg} > 0 then
          {System.printInfo "Arguments: "}
          {System.show {Record.toListInd Msg}}
       end
    end
 end

 Object = {New Example init}
in
 {Object foo}
 {Object bar}
 {Object grill}
 {Object ding(dong)}</lang>

Output:

foo
bar
Unknown method grill
Unknown method ding
Arguments: [1#dong]

Perl

<lang perl>package Example; sub new {

   bless {}

} sub foo {

   print "this is foo\n";

} sub bar {

   print "this is bar\n";

} sub AUTOLOAD {

   my $name = $Example::AUTOLOAD;
   my ($self, @args) = @_;
   print "tried to handle unknown method $name\n";
   if (@args) {
       print "it had arguments: @args\n";
   }

} sub DESTROY {} # dummy method to prevent AUTOLOAD from

                       # being triggered when an Example is
                       # destroyed

package main; my $example = Example->new;

$example->foo; # prints "this is foo" $example->bar; # prints "this is bar" $example->grill; # prints "tried to handle unknown method Example::grill" $example->ding("dong"); # prints "tried to handle unknown method Example::ding"

                       # and "it had arguments: dong"</lang>

PHP

<lang php><?php class Example {

 function foo() {
   echo "this is foo\n";
 }
 function bar() {
   echo "this is bar\n";
 }
 function __call($name, $args) {
   echo "tried to handle unknown method $name\n";
   if ($args)
     echo "it had arguments: ", implode(', ', $args), "\n";
 }

}

$example = new Example();

$example->foo(); // prints "this is foo" $example->bar(); // prints "this is bar" $example->grill(); // prints "tried to handle unknown method grill" $example->ding("dong"); // prints "tried to handle unknown method ding"

                       // prints "it had arguments: dong

?></lang>

PicoLisp

The function 'try' is used to send a message to an object for which it is not known whether it inherits a method for that message or not. As opposed to the syntacically equivalent 'send' function, 'try' does not give an error, but returns NIL. <lang PicoLisp>(unless (try 'unknown> Obj)

  (anotherMethod> Obj) )</lang>

<lang PicoLisp>(or

  (try 'message1> Obj)
  (try 'message2> Obj)
  (try 'message3> Obj)
  (send *FinalMessage Obj) )</lang>

Python

Python objects can implement a __getattr__() method to handle accesses of unknown attributes (methods are just attributes that are callable; so this function handles both methods and non-method fields). Here we assume that if you access an unknown attribute, you want a method, so we return a function that can be called. <lang python>class Example:

   def foo(self):
       print "this is foo"
   def bar(self):
       print "this is bar"
   def __getattr__(self, name):
       def method(*args):
           print "tried to handle unknown method " + name
           if args:
               print "it had arguments: " + str(args)
       return method

example = Example()

example.foo() # prints “this is foo” example.bar() # prints “this is bar” example.grill() # prints “tried to handle unknown method grill” example.ding("dong") # prints “tried to handle unknown method ding”

                    # prints “it had arguments: ('dong',)”</lang>

Ruby

<lang ruby>class Example

   def foo
       puts "this is foo"
   end
   def bar
       puts "this is bar"
   end
   def method_missing(name, *args)
       puts "tried to handle unknown method %s" % name # name is a symbol
       unless args.empty?
           puts "it had arguments: %p" % [args]
       end
   end

end

example = Example.new

example.foo # prints “this is foo” example.bar # prints “this is bar” example.grill # prints “tried to handle unknown method grill” example.ding("dong") # prints “tried to handle unknown method ding”

                    # prints “it had arguments: ["dong"]”</lang>

Scala

Works with: Scala version 2.9

As of scala 2.9, scalac must receive the -Xexperimental optional for Dynamic to receive this treatment. <lang scala>class DynamicTest extends Dynamic {

 def foo()=println("this is foo")
 def bar()=println("this is bar")
 def applyDynamic(name: String)(args: Any*)={
   println("tried to handle unknown method "+name)
   if(!args.isEmpty)
     println("  it had arguments: "+args.mkString(","))
 }

}

object DynamicTest {

 def main(args: Array[String]): Unit = {
   val d=new DynamicTest()
   d.foo()
   d.bar()
   d.grill()
   d.ding("dong")
 }

} </lang>

Slate

Here is an example of unknown methods being used to call shell commands (this is already defined in the base image):

<lang slate>define: #shell &builder: [lobby newSubSpace].

_@shell didNotUnderstand: message at: position "Form a command string and execute it." [

 position > 0
   ifTrue: [resend]
   ifFalse:
     [([| :command |

message selector isUnarySelector ifTrue: [command ; message selector. message optionals pairsDo: [| :key :value | command ; ' -' ; (key as: String) allButFirst allButLast ; ' ' ; (value as: String)]]. message selector isKeywordSelector ifTrue: [| keywords args | keywords: ((message selector as: String) splitWith: $:). command ; keywords first. keywords size = 1 ifTrue: "Read a string or array of arguments." [args: message arguments second. (args is: String) ifTrue: [command ; ' ' ; args] ifFalse: [args do: [| :arg | command ; ' ' ; arg]]]]] writingAs: String) ifNil: [resend] ifNotNilDo: [| :cmd | [Platform run: cmd]]] ].</lang>

Here is an example of it being used:

<lang slate>slate[1]> shell ls: '*.image'. kernel.new.little.64.1244260494374694.image slate2.image net.image slate.image True slate[2]></lang>

Smalltalk

Works with: GNU Smalltalk

<lang smalltalk>Object subclass: CatchThemAll [

   foo [ 'foo received' displayNl ]
   bar [ 'bar received' displayNl ]
   doesNotUnderstand: aMessage [
     ('message "' , (aMessage selector asString) , '"') displayNl.
     (aMessage arguments) do: [ :a |
       'argument: ' display. a printNl.
     ]
   ]

]

|a| a := CatchThemAll new. a foo. a bar. a weCanDoIt. a theyCanToo: 'eat' and: 'walk'.</lang>

Tcl

Works with: Tcl version 8.6

or

Library: TclOO

<lang tcl>package require TclOO

  1. First create a simple, conventional class and object

oo::class create Example {

   method foo {} {
       puts "this is foo"
   }
   method bar {} {
       puts "this is bar"
   }

} Example create example

  1. Modify the object to have a custom ‘unknown method’ interceptor

oo::objdefine example {

   method unknown {name args} {
       puts "tried to handle unknown method \"$name\""
       if {[llength $args]} {
           puts "it had arguments: $args"
       }
   }

}

  1. Show off what we can now do...

example foo; # prints “this is foo” example bar; # prints “this is bar” example grill; # prints “tried to handle unknown method "grill"” example ding dong; # prints “tried to handle unknown method "ding"”

                  # prints “it had arguments: dong”</lang>