Does anyone know of an OO language where objects h...
# thinking-together
s
Does anyone know of an OO language where objects have externally explicit namespaces for their protocols? For example, instead of something like “aView.onDropEnter(…)” it might look like “aView.dropping.onEnter(…)“.
👍 1
d
CLOS, the Common Lisp Object System. A class method is called a "generic function", and a namespace is called a "package". The syntax for calling a method is
(dropping:onEnter aView ...)
.
👍 3
s
So in that function, does it have a giant case statement for every type that might implement the protocol?
d
It looks like a giant case statement from the outside, I guess. That's not how the code is written. You use
(defclass View ...)
to define a class, you use
(defgeneric onEnter (obj ...) ...)
to define a protocol, and you use
(defmethod onEnter ((obj View) ...) ...)
to implement a protocol for a specific class. Methods are not defined inside the class, they are defined external to the class definition.
k
A table lookup, which is just one optimization away from a case statement. One key difference: it's extensible. You define new tables with
defgeneric
and new rows in a table with
defmethod
.
There are also predicate dispatch systems out there where you can't use a table and are forced to fall back to a giant (but still extensible) case statement. https://en.wikipedia.org/wiki/Predicate_dispatch
s
It doesn’t make sense to me why it would be organized this way, but then I also don’t understand why the unix file system is organized by function (where installing an app scatters related files across the system instead of keeping them in a single folder).
d
Classes are not the natural unit of modularity. Often they are, but it's just as common for traditional OOP languages to force you to violate modularity when they insist on grouping all methods for a class inside the same module. Common Lisp provides additional flexibility in how you can organize your code. You can put all the methods for a class into the same source file or package as the class itself, if it makes sense, but you aren't forced to do that.
d
Go (aka golang) allows methods for anything to be defined anywhere. Haskell typeclasses / instances are somewhat like that. JavaScript too, in that you can use constructors like mixins, by "apply"ing them to any object -- though technically you can apply any "method" to any object at all.
s
@Dan Cook yeah, I was thinking about implementing this in Javascript. On mixins, don’t they typically shared the class’ method namespace?
t
This is a pattern that we often use in Pharo and GT.
👍 2
d
@Steve Dekorte There are three separate things going on when you talk about methods and namespaces: 1. A method is just a function that takes an extra (invisible) argument called "this", for the object that it is acting on. 2. The conceptual idea of a "method" is that is an actual property (member) of the specific object that it "belongs to". But in most languages, a single function is shared for every instance-object of the same class. So if x and y are both instances of class C, then x.foo(123) and y.foo(456) are really just funny syntax for calling C.foo(x,123) and C.foo(y,456), where x and y are the "this" parameter I described in #1 3. There's the actual container (namespace or parent-object) of the method itself. In #2, the "too" method is contained in class C, although the syntax for calling it makes it look like it is contained in x and y. In most (statically typed) OO languages, these concepts are all munged together: the method is a member of the class and (conceptually) of it's instances, and the fact that "this" is passed as an argument is hidden away by the compiler.
In JavaScript, x.foo(123) means that there is an actual property of x, which is then being called like a function. If "foo" is just a normal (non-method) function, then this is the same as doing: var f = x.foo; f(123); // same as doing: x.foo(123); However, JacaScript will also set "this" to whatever object you accessed the function from, so the one way will set this=x, and the other will not. But you can explicitly set "this" using function.call(this, args...): var x = { a: 0 }; var y = { a: 5 }; var f = function(b) { return this.a + b; }; x.foo = f; y.foo = f; x.foo(1); // this=x, so 1 y.foo(1); // this=y, so 6 f.call(x, 1); // this=x, so 1 f.call(y, 1); // this=y, so 6 x.foo.call(x, 1); // this=x, so 1 x.foo.call(y, 1); // this=y, so 6 y.foo.call(x, 1); // this=x, so 1 y.foo.call(y, 1); // this=y, so 6
In C#, you can define "extension methods" which are like extra methods for another class, but defined somewhere else. Really they are just normal functions ("static methods"), but by putting "this" before the first argument, the compiler will let you call it as if it were a method of that first argument: int Foo(this Bar b, int x) { ... } Bar myBar; ... Foo(myBar, 5); myBar.Foo(5); // really the same as above
y
The interface could always have a single method that returns a dictionary of “methods”. I’ve used this approach with Haskell type-classes (which are technically more powerful than interfaces) in cases where it reduced boilerplate (where instead of the instance implementing four simple boilerplate methods it just needs to implement the one)
d
There's also the question of, what is it you are really trying to do? Research mixins and traits (not what some languages CALL these things, e.g. not in Scala), and you'll find ways that "methods" can be added to classes without actually creating inheritance hierarchies. It's just about grouping methods into one place and then telling the compiler that you want those methods in whichever classes. Look for the scharli paper. DCI also does some interesting things with method-injection, but it's hard to understand and probably better thought of as taking the idea of C# extension methods and applying it to more areas (like a dependent type): int Foo(Bar b, b.(int):string bMethod) { ... b.bMethod(...) } That again, is something that can be handles as a static compiler trick, so that no actual "method" exists at runtime.
s
@Tudor Girba What does that look like in Smalltalk?
t
In Smalltalk they are called extension methods 🙂. This is about packaging methods in a separate place then where the class is defined. Traits also happened to have been invented in Smalltalk, and they are about grouping logical behavior.
d
How about syntax like this: obj.(Other.Namespace.methodForObj)(args) Or: x = methodForObj obj.(x)(args)
s
@Tudor Girba Can you provide an example of the syntax?
t
Syntax for what specifically?
s
For namespace isolated methods that implement a protocol for an object/class/prototype as I described in my question that started this thread.
d
I was talking about a hypothetical syntax for a new language. The difference is: x.normalMethodOfX(args); x.(expression)(args); Where "expression" is any expression that returns a method. Technically you would also be about to do: x.(x.normalMethodOfX)(args)
t
I now think I misinterpreted the question. When we want to scope, we create an object that points to the original object.
in the recent Pharo, there is work towards productizing Talents, which are object-specific traits that you can add at runtime
l
I use a graph structure where functions may be defined on the relation, eg.
<http://List.to|List.to> String: ...
is semantically same as
String.from List: ...
, you may also organize it by putting an extension of one type in another, eg.
<http://String.List.to|String.List.to> String: ...
. Because of the structured editor, the definitions are visible and may be edited in all relevant places. Because the actual reference is saved in the graph, a later signatures overlap won't break the existing logic, though possibly change the presentation to make it more apparent which relation the signature belongs to.