I have been programming in Smalltalk for some time, but I never really needed interfaces to implement anything. Then why can't languages such as Java get rid of interfaces? Is it only Smalltalk or is there another language which doesn't need interfaces?
Because Java is statically typed while Smalltalk is not. Interfaces don't serve any purpose when you don't declare types and your variables aren't going to be typechecked. But in a statically typed language like Java, they're extremely handy, because they let you have a variable whose type is defined by the methods the object implements instead of its class. It brings you a lot closer to the dynamic typing Smalltalk has natively without giving up the benefits of typechecking.
It is a polymorphism issue: in Java you have static types and therefore you need to know which messages can your object answer... in Smalltalk (and other non-static languages) you just need to implement right methods to have polymorphism.
For instance:
- In Java you need to implement Cloneable, who defines method Cloneable.clone to have cloneble objects. Then, the compiler knows your object will understand that method (otherwise it will throw an error)
- In smalltalk, you just need to implement method #clone. Compiler never knows/don't care about which messages understands your objects until it is called.
That also means you can have polymorphic objects without being part of same hierarchy... multi inheritance, mixins and other approachs (traits are present on Pharo) are just reuse technics, not a design constraint.
This way of do things is often called "duck typing"... see: http://en.wikipedia.org/wiki/Duck_typing
write()
, read()
and your class is file-like. But today Python community got that it's a bad idea and added protocols. If I have an object with #clone
method, I have not idea is it a clonable object or this "clone" means something different. Another example: if I have write
method, does it mean that I have open
and close
methods too? Only interface is the contract. Otherwise your code will be polluted by a lot of runtime checks (with reflection) of some methods existence and checking of their args/signatures/arity. –
Girlie Do you think there might be a useful role for "interfaces" in Smalltalk?
not sure what exactly your asking (or rather, which question you most want answered) but have a look at Ruby. From my understanding it's much closer to smalltalk than Java is.
If i were to answer the question about why java needs interfaces, I guess I'd say something about java being a statically typed language and taking that philosophy to the extent that java does is what makes for the need of interfaces. Effectively interfaces try to give you something like multiple inheritence in java without the multiple inheritance issues that other languages face (C++ i believe).
Java has no need for interfaces. It is something the compiler chose to support rather than discard.
At runtime, interfaces cannot be enforced in any language because all dynamic objects are either 1. structs of pure state or 2. structs of pure state with first member being a pointer to a vtable mapping either integers to members(via array) or strings to members(being dictionary/hashmap). The consequence of this is that you can always change the values at indices of the vtable, or entries of the hashmap, or just change the vtable pointer to another vtable address, or just illegal access memory.
Smalltalk could have easily stored information given at compile time of your classes, and in a way it does, that's how intellisense in smalltalk browsers gives member suggestions, but this would not actually benefit smalltalk.
There are several issues with smalltalk's syntax that limits the use of interfaces.
- Smalltalk has only one primary type
This means that it can't just warn you if you try putting a square into a circle hole, there are no squares, there are no holes, everything is an object to the smalltalk compiler.
The compiler could choose to type deduce variables that were assigned, but smalltalk philosophically objects to doing so.
- Smalltalk methods always take one argument
It might seem like doing myArray at: 1 put: 'hi'
has two arguments, but in reality, you are calling a javascript equivelent of myArray['at:put:']([1, 'hi']) with myArray being an object(~hashmap). the number of arguments thus cannot be checked without breaking the philosophy of smalltalk.
there are workarounds smalltalk could do to check number of arguments, but it would not give much benefit.
- smalltalk exposes its compiler to runtime, whereas java tries very hard to bury the compiler from runtime.
When you expose your compiler to runtime(all languages from assembly to javascript can easily expose their compiler to runtime, few make it part of the easily accessible parts of the language, the more accessible the compiler is at runtime, the higher level we consider the language to be), your language becomes a tad more fragile in that the information you use at compile time on one line, may no longer be valid on another line because at runtime, the information compiler relied on being fixed is no longer the same.
One consequence of this is that a class might have one interface at one point of the program, but half way into the program, the user changed the class to have another interface; if the user wants to use this interface at compile time(after changing the class using code), the compiler needs to be much smarter to realize that the class that didn't support ".Greet()" now suddenly does, or no longer does, or that the method ".Greet()" and method ".Foo()" have been swapped around.
interfaces are great at compile time, but completely unenforceable at runtime. This is great news for those that want to change behavior of code without needing to restart the program, and horrible news for type safety purists - their ideals simply can't be enforced at runtime without manually poking every assertion at an interval.
unlike C++, smalltalk does not use arrays for vtables, instead it uses maps from strings to objects. This means that even if you do know that the method exists in the class you're calling, you cannot optimize it to a dispid so that future calls to this method use array offset instead of hashing to find the method. To demonstrate this, let's use javascript:
The current smalltalk objects behave analogous to this:
var myIntVtbl = { '+': function(self, obj1) { return {lpVtbl: myIntVtbl, data: self.data + obj1.data}; } }; var myInt1 = {lpVtbl: myIntVtbl, data: 2}; var myInt2 = {lpVtbl: myIntVtbl, data: 5}; var myInt3 = myInt1['lpVtbl']['+'](myInt1, myInt2); var myInt4 = myInt3['lpVtbl']['+'](myInt3, myInt3); var myInt5 = myInt4['lpVtbl']['+'](myInt4, myInt4); console.log(myInt5);
each time we call +, we must hash '+' to get the member from the vtable dictionary. Java works similarly, which is why decompilers can tell the names of methods so easily.
one optimization that a compiler can do if it knows interfaces, is to compile strings to dispids, like so:
var myIntVtbl = [ function(self, obj1) { return {lpVtbl: myIntVtbl, data: self.data + obj1.data}; } ];
var myInt1 = {lpVtbl: myIntVtbl, data: 2}; var myInt2 = {lpVtbl: myIntVtbl, data: 5}; var myInt3 = myInt1['lpVtbl'][0](myInt1, myInt2); var myInt4 = myInt3['lpVtbl'][0](myInt3, myInt3); var myInt5 = myInt4['lpVtbl'][0](myInt4, myInt4); console.log(myInt5);
as far as I know, neither java nor smalltalk do this for classes, whereas C++, C#(via comvisible attribute) do.
To summarize, smalltalk can use interfaces, in turn becoming more like PHP, but won't get pretty much any benefit of it at compile time other than weak reassurances.
Likewise, Java doesn't need interfaces, it can literally work like smalltalk under the condition that you expose the java compiler to java to be more accessible. To get a feel for this, you can interop between java and nashorn javascript engine that comes with all current java kits and use its' eval function as a runtime compiler. Java can easily get rid of interfaces, and use reflective polymorphism, treat everything as Object, but it'll be much more verbose to talk to objects without letting you index by string, and overloading the index operator for string to dynamically find the members.
© 2022 - 2024 — McMap. All rights reserved.
traits
instead of interfaces. Traits are like abstract classes, but with a few limitations, to allow for multiple inheritance. They are all good: scala-lang.org/node/126 – Maunder