12/14/2005

 

Dynamic Overloading

Haven't heard of dynamic overloading? Yes, I made it up:) It is not only another way to support polymorphism, but it is also a revolution to the Object-Oriented Programming. It brings the functions and the semantic of the objects back to the center of stage to replace the position of "class" and "inheritance". First of all, let's look at static overloading.
public class Sample {
 public static void main(String[] args) {
  new Sample().demo();
 }
 public void demo() {
  func(new A());
  func(new B());
 }
 public void func(A arg) {
  System.out.println(arg);
 }
 public void func(B arg) {
  System.out.println(arg);
 }
 class A {
  public String toString() {
   return "hello from A";
  }
 }
 class B {
  public String toString() {
   return "hello from B";
  }
 }
}
Because the two "func" have different signature i.e. the "arg" has different type defined by different "class", the compiler can resolve the invokation to the concrete function, so we call it static. Then, let's look at overriding(Maybe this is an inexact word).
public class Sample {
 public static void main(String[] args) {
  new Sample().demo();
 }
 public void demo() {
  A a = new B();
  a.func();
 }
 class A {
  public void func() {
   System.out.println("hello from A");
  }
 }
 class B extends A {
  public void func() {
   System.out.println("hello from B");
  }
 }
}
Then, let's check the duck typing case(Written in Ruby):
class A
 def func()
  puts "hello from A"
 end
end

class B < A
 def func()
  puts "hello from B"
 end
end

obj = B.new()
obj.func()
Oops~ It is duck typing, we don't need "class B < A" actually. As long as the object has a slot named "func", it is ok, the hindrance set by the static type system has disappeared.
class A
 def func()
  puts "hello from A"
 end
end

class B
 def func()
  puts "hello from B"
 end
end

def demo(obj)
 obj.func()
end

demo(B.new())
demo(A.new())
If we use Javascript(prototype language), we even don't need to define a class to set the slots of its objects. We can do things like "obj.func = someFunc", then we can call it from the obj. Ok, right now, we have seen the static overloading and overriding state of arts. Given it is static, static overloading won't be a reasonable choice to be the main stream polymorphism. And, all the overridings are based on a assumption, we can dynamically get a function pointer from a slot of the object, then call it. Using prototype doesn't need you to define the slot in a class, you can just add it to the object in the runtime, but the object still knows what operation it can apply. If we want to call a method of a object, you has to set the slot before you call it. This limitation is the motivation of inventing dynamic overloading. Now, let's examine polymorphism, in general. We know overloading and overriding are both ways to be polymorphism. All polymorphism is about is binding a call to different function. Overloading judging it by static signature. Overriding judging it by value of the slot in the object. How about judging it by "the semantic of the object"? I mean every function describes what the object it operates on should be like. Then in the runtime, depending on the actual form of object, we can decide which function to call. As I haven't invented a full system to implement the idea, I can only demonstrate it in pseudo-code:
//in file func.fluffy
var a;
function func() {
 puts this.a;
}
var b;
function func() {
 puts this.b;
}
//in file createA.fluffy
var a;
function createA() {
 this.a = "hello from A";
 return this;
}
//in file createB.fluffy
var b;
function createB() {
 this.b = "hello from B";
 return this;
}
//in file demo.fluffy
function demo() {
 var obj = createA();
 obj.func();
 obj = createB();
 obj.func();
}
I know this example is rather weird, because it is so different~ Let's check it line by line: var obj = createA(); It invokes createA(). Because we don't call it using a object, so the runtime will create a empty object for you. So the "this" in createA is not null, but a empty object. We declared the object createA() bound to has a field named "a"(which is not only means a member variable named "a" but carries some semantic meaning in real case, such as "frequentAccount" means something in biz), so the empty "this" has a field "a". Then, we return "this". Ok, right now, we just created a object has a field named "a". obj.func(); Then we try to apply "func" on "obj". This line doesn't try to get a function pointer from the slot "func" from the "obj", instead it actually is func(obj). Then the dynamic overloading happened. We have two "func" in hand, one expects a object with a field named "a", the other expects a object with a field named "b". It is easy to decide which "func" to call. It is the first one, because right now, the "obj" is a object with a field "a". The following lines are rather similar. We can see which caused we choosing the first "func", that is the semantic of the object, how many fields the object has and the meaning of each field means(shown by the name of the field). Further more, we can introduce a mark to show semantic equivalence.
//in file createArticle.fluffy
var title;
var author;
var content;
function createArticle() {
 this.author = "wen tao";
 return this;
}
//in file getFirstName.fluffy
@string
@author
var name;
function getFirstName() {
 return this.name.split(" ")[0];
}
//in file demo.fluffy
function demo() {
 createArticle().getFirstName();
}
In theory above, the object created by "createArticle" can not be accpeted by getFirstName(), because object(title, author, content) doesn't have a field named "name". But notice this: @author var name; that means "name" can be replaced with "author", but NOT vice versa. Then plus a namespace mechanism, we have a full-blown language which I call "Fluffy" :) see you, it is long enough. Hope you have grasped the main point.

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]