That doesn't mean you have to give up a compiler. Imagine this scenario. You have a language. That language allows you to define a class thusly:
class Runner
method RunSynchronously(z)
z.Start()
return z.Stop();
end
end
Then you have some other classes, of which instances are passed into the method defined above:
class A # null object pattern
method Start()
end
method Stop()
return true
end
end
class B # normal implementation
method Start()
workReceipt = ThreadPool.StartWorking(someBehavior)
end
method Stop()
return ThreadPool.WaitForComplete(workReceipt)
end
end
class C # asynchronous no matter what implementation
method Start()
ThreadPool.StartWorking(someBehavior)
end
method Stop()
return false;
end
end
How could that be compiled? How could it be statically type checked? Nobody defined an abstraction, right? Wrong.
An abstraction was defined implicitly and those objects do conform to it. The Runner class expects its z parameter for RunSynchronously to have a Start method and a Stop method. That is a de facto interface. There is no reason it cannot be enforced by a compiler. There are lots of reasons why it should.
Maybe when I get some more free time, I'll look into it.