The Artima Developer Community
Sponsored Link

Artima Developer Spotlight Forum
Namespacey Programming

30 replies on 3 pages. Most recent reply: Dec 12, 2007 1:21 PM by nes

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 30 replies on 3 pages [ 1 2 3 | » ]
Michael Hobbs

Posts: 51
Nickname: hobb0001
Registered: Dec, 2004

Namespacey Programming Posted: Oct 10, 2007 1:09 PM
Reply to this message Reply
Advertisement
The recent discussions on Artima about Erlang led me to Joe Armstrong’s essay, “Why OO Sucks.” That got me thinking deeply about what object-oriented programming (OOP) really provides and why functional languages typically don’t embrace it. The textbook definition of OOP is that it provides encapsulation, inheritance, and polymorphism. But taking away the big polysyllabic words, what is the essential value that OOP provides? It turns out that at its basic quantum level, an object is just a namespace – nothing more. One could say that in OOP, namespaces are first-class values.

Let’s return to those $60/hr words and see how they describe different aspects of first-class namespaces. First, it’s fairly easy to see how namespaces provide encapsulation. It’s actually the defining feature of a namespace. That is, a namespace is a collection of variable (and function) names. Contrary to many common beliefs, encapsulation is not a new concept invented by OOP. C has structs, Pascal has records, and Lisp has closures.

OOP namespaces really break away from “classical” namespaces in that they have dynamic scope. That is, not only do they inherit the static lexical scope of the enclosing code, they can also inherit variables from any other arbitrary namespace, nee object. With some OOP languages, this inheritance can even be dynamically modified at runtime.

Lastly, polymorphism is the big daddy of OOP where everything else comes together. Let’s look at the prototypical OOP expression, “shape.area()”, to see what’s happening from a namespace point of view. The expression takes the namespace bound to the variable “shape”, looks in it for the first function named “area” and then evaluates that function in the scope of the namespace. There is one important point in that description: the area() function is evaluated in the scope of the “shape” namespace. If, say, the area() function references a variable named “radius”, that variable is looked up in the “shape” namespace, not the one in which area() was defined. Once you understand how this works, you understand OOP.

Now that we have the basic quantum mechanics of OOP, let’s circle back to the original question of why functional languages typically don’t use it. I think the most fundamental reason is that functional programmers don’t like the spooky action at a distance that can happen with dynamic namespaces. Functional programmers love their lexical closures. They can see at a glance all the variables names that are in scope and what’s being assigned to them. There is no unknown state. In the cases were OOP is introduced into functional programming, therefore, they will likely employ the idiom of Python and OCaml whereby the namespace is explicitly passed as a parameter to the function, rather than the conventional method whereby the function is surreptitiously evaluated in a different namespace.

Lastly, I’d like to propose a name change, since the word "object" is so vague. In the OOP tradition of using clear names, therefore, a more accurate and descriptive name for OOP might be namespace-oriented programming. Or to rip-off the grammar employed by “functional” programming, maybe it could be called namespatial programming. I personally prefer the more plebeian, namespacey programming.


Frank Silbermann

Posts: 40
Nickname: fsilber
Registered: Mar, 2006

Re: Namespacey Programming Posted: Oct 11, 2007 7:15 AM
Reply to this message Reply
One aspect of some object-oriented languages is an interface-based type system. That's why I've always thought of OO as "interface-oriented programming".

I don't think polymorphism scares functional programmers; after all, ML pioneered the concept of polymorphism. I think rather the objection (no pun intended) is that objects encapsulate state, whereas pure functional programs are stateless.

robert young

Posts: 361
Nickname: funbunny
Registered: Sep, 2003

Re: Namespacey Programming Posted: Oct 11, 2007 9:38 AM
Reply to this message Reply
> I think rather the objection (no pun intended) is that
> objects encapsulate state, whereas pure functional
> programs are stateless.

One of the many ironies of these sort of discussions is that, oh 99.44% of, OO programmers write to web frameworks (servlets, for example). Such frameworks (and at times the client programmers) jump through hoops to make their code stateless; "put all the data in doGet".

Larry Bugbee

Posts: 13
Nickname: bugbee
Registered: Dec, 2004

Re: Namespacey Programming Posted: Oct 11, 2007 9:49 AM
Reply to this message Reply
One reason I like Python, and not to start a language war, is that I can write OO, procedural, and "functional" in the same program, using that which makes the most sense when and where I need it. Other languages force you into their model forcing you to fit the square peg into the round hole. I appreciate the flexibility.

Miguel Angel Hernández Orozco

Posts: 6
Nickname: migsho
Registered: Sep, 2007

Re: Namespacey Programming Posted: Oct 11, 2007 9:53 AM
Reply to this message Reply
I think you got a point here. How do you use namespaces in JavaScript? With objects, thus objects can be as namespaces... but I certainly don't think a Namespace Oriented Programing is more intuitive than the current one.

Michael Hobbs

Posts: 51
Nickname: hobb0001
Registered: Dec, 2004

Re: Namespacey Programming Posted: Oct 11, 2007 10:53 AM
Reply to this message Reply
> I don't think polymorphism scares functional programmers;
> after all, ML pioneered the concept of polymorphism. I
> think rather the objection (no pun intended) is that
> objects encapsulate state, whereas pure functional
> programs are stateless.

One of the points that I should have made explicit in the first place is that I don't think it's a matter of stateful or stateless. Namespaces can be immutable. The issue is more of predictability. Functional programmers expect a certain amount of runtime predictability. If you allow a caller to swap in a totally different namespace than what you were expecting - even if the namespace is immutable, it screws with your sense of predictability.

Though now that I've talked it out, I've come around to thinking that this just may be a different way of expressing statefulness. That is, even if you have totally immutable namespaces/states, but still allow the ability to surreptitiously swap out one namespace with another, the namespace may as well be mutable to begin with.

Brandon Corfman

Posts: 14
Nickname: bcorfman
Registered: Aug, 2003

Re: Namespacey Programming Posted: Oct 11, 2007 11:05 AM
Reply to this message Reply
It's interesting you should mention this ... I was writing a Python class last night, and the only methods I added to it were the ones that I wanted visible. The "private" methods (i.e. the ones that start with an underscore) I pulled outside the class and made regular functions instead. I don't like the way OOP encourages you to make variables part of your class "namespace". In a language like Python that has a REPL, storing state in instance variables makes it more cumbersome to test your methods. I'd much rather have a functional paradigm for testing (pass all needed state values as parameters, check the output value(s) for correctness).

Basically I like managing state at the function level, not the class level. There's too much risk of getting unexpected coupling at the class level, IMO.

Alex Stojan

Posts: 95
Nickname: alexstojan
Registered: Jun, 2005

Re: Namespacey Programming Posted: Oct 11, 2007 11:15 AM
Reply to this message Reply
> > I don't think polymorphism scares functional
> programmers;
> > after all, ML pioneered the concept of polymorphism. I
> > think rather the objection (no pun intended) is that
> > objects encapsulate state, whereas pure functional
> > programs are stateless.
>
> One of the points that I should have made explicit in the
> first place is that I don't think it's a matter of
> stateful or stateless. Namespaces can be immutable. The
> issue is more of predictability. Functional programmers
> expect a certain amount of runtime predictability. If you
> allow a caller to swap in a totally different namespace
> than what you were expecting - even if the namespace is
> immutable, it screws with your sense of predictability.
>
> Though now that I've talked it out, I've come around to
> thinking that this just may be a different way of
> expressing statefulness. That is, even if you have totally
> immutable namespaces/states, but still allow the ability
> to surreptitiously swap out one namespace with another,
> the namespace may as well be mutable to begin with.

I'm not sure I understand your predictability argument. Consider the following Scheme program:

(define (f some-function) (some-function))

By looking at function 'f' you can't tell what it's going to return, because that depends on what 'some-function' is. However, this idiom is still quite often used in Scheme.

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Namespacey Programming Posted: Oct 11, 2007 11:43 AM
Reply to this message Reply
> Let’s look at the
> prototypical OOP expression, “<code>shape.area()</code>”,
> to see what’s happening from a namespace point of view.
> The expression takes the namespace bound to the variable
> “<code>shape</code>”, looks in it for the first function
> named “<code>area</code>” and then evaluates that function
> in the scope of the namespace. There is one important
> point in that description: the <code>area()</code>
> function is evaluated in the scope of the
> “<code>shape</code>” namespace. If, say, the
> <code>area()</code> function references a variable named
> “<code>radius</code>”, that variable is looked up in the
> “<code>shape</code>” namespace, not the one in which
> <code>area()</code> was defined. Once you understand how
> this works, you understand OOP.

This description of polymorphism is fairly odd. First not all languages that are classified as OO work that way and secondly it seems to be all about scope and not really about polymorphism.

Java for one, does not exhibit the behavior you describe. If the area() method uses a radius variable in a Java class, that variable will need to exist in the scope where the method is defined and will always use that variable when it is executed. In Python, where the behavior you describe would occur, the reason, as I understand it, isn't a result of polymorphism but rather that python doesn't make any distinction between the radius defined in the sub class and the radius defined in the super class. Both refer to the same 'slot' in the object. The second definition of radius is no more special than assigning the value of radius on that object from anywhere else in the program. The whole __ mangling hack is a way to work around that.

The other thing that is confusing about your example is that it doesn't make much sense for a generic area method to refer to a radius at all. What's the radius of trapezoid? The whole point of polymorphism in this context is that we are declaring that shapes have area but how that area is calculated depends on the kind of shape. Polymorphism allows us to get the area of any shape without knowing what shape it is.

Personally, I would think your explanation is going to confuse people that don't understand OO more than anything.

Michael Hobbs

Posts: 51
Nickname: hobb0001
Registered: Dec, 2004

Re: Namespacey Programming Posted: Oct 11, 2007 12:30 PM
Reply to this message Reply
> I'm not sure I understand your predictability argument.
> Consider the following Scheme program:
>
> (define (f some-function) (some-function))
>
> By looking at function 'f' you can't tell what it's going
> to return, because that depends on what 'some-function'
> is. However, this idiom is still quite often used in
> Scheme.

In that scenario, you know that 'some-function' is passed as an argument, so you know when you see it that you can't tell what it will return. My predictability argument applies to something more like this:

(define (double x) (+ x x))
(define (f a b) (* (double a) (double b)))

In this case, the definition of f() relies on the definition of double(), which is in its lexical scope. Now, if I'm able to call f() with something like this, the behaviour of f() becomes extremely unpredictable:

(define (zero x) 0)
(with ((double zero)) (f 1 2))

Where "with" is like "let" except that it changes the scope with which f() itself is executed. That is, instead of f() pulling the definition of double() from its lexical scope, it instead pulls it from the "with" scope, where double() has been redefined to refer to zero().

Michael Hobbs

Posts: 51
Nickname: hobb0001
Registered: Dec, 2004

Re: Namespacey Programming Posted: Oct 11, 2007 1:10 PM
Reply to this message Reply
> This description of polymorphism is fairly odd. First not
> all languages that are classified as OO work that way and
> secondly it seems to be all about scope and not really
> about polymorphism.
>
> Java for one, does not exhibit the behavior you describe.
> If the area() method uses a radius variable in a Java
> a class, that variable will need to exist in the scope
> where the method is defined and will always use that
> variable when it is executed.

Yeah, OOP languages derived from C++, such as Java, are kinda' odd in that polymorphism only applies to methods, not to fields. So if we redefine radius as a method, my example will hold:

class Circle extends Shape {
public double radius() {
// Default to a unit circle.
return 1;
}
public double area() {
return Math.PI * radius() * radius();
}
}
class LargeCircle extends Circle {
public double radius() { return 1000; }
}


> The other thing that is confusing about your example is
> that it doesn't make much sense for a generic area method
> to refer to a radius at all.

You're right in that that wasn't the best example. Even the Circle and LargeCircle examples above are rather contrived.

Let me try again. Instead of a Shape.area() method that references radius, let's go with an ExtrudedShape.volume() method that references area():

abstract class ExtrudedShape implements Shape,Volume {
protected double length;
public double volume() { return length * area(); }
}
class Cylinder extends ExtrudedShape {
protected double radius;
public double area() {
return Math.PI * radius * radius;
}
}
class Box extends ExtrudedShape {
protected double width;
protected double height;
public double area() {
return width * height;
}
}

Now, if I call cylinder.volume(), it will invoke the volume() function using the namespace defined by the cylinder object, wherein area() is defined as PI*r*r. If I call box.volume(), the function will use a namespace wherein area() is defined as width*height.

I hope that helped clarify my example?

James Watson

Posts: 2024
Nickname: watson
Registered: Sep, 2005

Re: Namespacey Programming Posted: Oct 11, 2007 1:55 PM
Reply to this message Reply
> Now, if I call cylinder.volume(), it will invoke the
> volume() function using the namespace defined by the
> cylinder object, wherein area() is defined as PI*r*r. If I
> call box.volume(), the function will use a namespace
> wherein area() is defined as width*height.
>
> I hope that helped clarify my example?

Sure but referring back to Alex's comment and ask whether this is any more surprising than his example?

Personally I see the OO as inside-out functional programming and vice-versa to a large degree. Obviously it's not that simple but I feel like attempts to show that that one is pathological and the other is not are really misguided. I don't think that was your intent but your initial example would make OO seem really screwed up to me if I didn't know much about it.

John Zabroski

Posts: 272
Nickname: zbo
Registered: Jan, 2007

Re: Namespacey Programming Posted: Oct 11, 2007 8:43 PM
Reply to this message Reply
@Michael Hobbs
@The textbook definition of OOP is that it provides encapsulation, inheritance, and polymorphism. But taking away the big polysyllabic words, what is the essential value that OOP provides?

Object-orientation provides method dispatch.

While I respect Joe Armstrong for inventing Erlang and think listening to him about concurrency has made me a better programmer, his words are not a substitute for critical thinking.

Looking carefully at his essay, his "Objection 2 - Everything has to be an object" example is absolutely awful. His example is precisely the "Objects Unencapsulated" bug pattern seen in production code. Assuming we are working with a specific type of Calendar system, the key point missing here is that components of a date have relational integrity constraint checks. What happens when you enter February 31st? By the type definitions provided in Joe's example, that's a legitimate date.

Joe's argument is further weakened by the fact he doesn't have any assertions of these invariants I mentioned. Type definitions are only one set of invariants a program usually conforms to. Typically, a piece of code conforms to a type definition, a set of assertions, and unit tests. Unit tests are almost essential, because they provide a level of formal detail on top of informal specifications we usually construct via user stories. There are four levels of specification: defined, implementation defined, unspecified, undefined. Unit tests help the maintenance programmer immensely in figuring out what is "defined" and what is merely "implementation defined". (You can also practice Cleanroom Software Engineering.) Any program will have to face itself with these four levels of specification. No reasonably large and sane program is entirely "defined".

As a general observation, object-oriented analysis & design often forces the practitioner to put his or herself in the direct pathway of the consequences of their actions. Greater accountability. However, OOA&D isn't some silver bullet and it requires deliberate effort to do right. The signs of health resulting from OOA&D is the result of superb programming: Superb programmers never put themselves in a position that would utterly test their skills. Yes, I'm making a generalization and all generalizations are false - but it's an important philosophy I keep.

I am not a skilled enough programmer to do Heroic Programming. I am not a Hero at the keyboard. I just recognize that, more often than not, The Problem Exists Between The Chair and the Keyboard. I put myself in the direct pathway of the consequences of my actions.

Netting it out: There is only one essential value that needs to be provided to all programmers: the ability to figure out what you want to say before you figure out how to say it. The problem is you can't just provide this to programmers, they have to motivate themselves to do it, and if they aren't examining the consequences of their actions then no programming paradigm will provide much value - and most of the value and quality will be from off-the-shelf software components that have succeeded in the market place to meet the needs of the unmotivated.

Miklos Hollender

Posts: 1
Nickname: shenpen
Registered: Oct, 2007

Re: Namespacey Programming Posted: Oct 12, 2007 3:46 AM
Reply to this message Reply
There are two interesting things happening in the OO world:

1) People are increasingly turning from is-a to has-a: specifying parameter signatures in interfaces as opposed to classes. It's a way of saying "I don't care what the type of your data is as long as it implements a method I want to call" or more simply: "I don't care about the type of your data, just it's behaviour"

2) The old CLOS idea of not binding methods tightly to classes but rather just defining generic functions and either throwing them into classes defined later on or even just leaving them as they are and relying on parametric polymorphism is slowly coming back. Some people are increasingly realizing that functions don't necessarily need to be tightly coupled with classes. Actually it's not very new, even C++ templates were a step towards this direction, albeit on a quite a low level.

So what do you get if you put this two together? "I don't care what type your data is as long as the generic function I want to call on it accepts this type". And of course that generic function is designed this way... and at the end of the chain you either have simple built-in types or custom types that are types, but not really objects in the real world sense (f.e. date is a type, but not really an object like a bird, a car or a sales order). This is the direction we are heading towards. I'm not sure what it is, but it's clearly NOT OO in the mainstream sense anymore.

Michael Hobbs

Posts: 51
Nickname: hobb0001
Registered: Dec, 2004

Re: Namespacey Programming Posted: Oct 12, 2007 7:33 AM
Reply to this message Reply
> > Now, if I call cylinder.volume(), it will invoke the
> > volume() function using the namespace defined by the
> > cylinder object, wherein area() is defined as PI*r*r. If
> I
> > call box.volume(), the function will use a namespace
> > wherein area() is defined as width*height.
> >
> > I hope that helped clarify my example?
>
> Sure but referring back to Alex's comment and ask whether
> this is any more surprising than his example?
>
> Personally I see the OO as inside-out functional
> programming and vice-versa to a large degree. Obviously
> it's not that simple but I feel like attempts to show that
> that one is pathological and the other is not are really
> misguided. I don't think that was your intent but your
> initial example would make OO seem really screwed up to me
> if I didn't know much about it.

No, it wouldn't be surprising if you're using Java. Java coders have grown accustomed to polymorphism swapping out method definitions.

If you're a functional programmer writing in Scheme, though, and you discovered that a function definition had been swapped out, you would be quite surprised. It goes against the grain of functional programming.

Let me see if I can show what Scheme might look like if we introduced polymorphism into it. To do so, I'll invent a couple of constructs. One is a function, (this), which returns the current closure as a first-class value. The second construct is (with ...), which is like (let ...), except that it allows you to pass in a closure to use in addition to the current lexical scope. Its form is: "(with closure (vars...) exprs...)".

; One possible way to define a constructor
; Returns a closure that contains x, double, and add-double
(define (obj-constructor x)
(define (double y) (+ y y))
(define (add-double y) (+ x (double y)))
(this))
; Create an "object" and invoke add-double
(define obj-5 (obj-constructor 5))
(with obj-5 () (add-double 3))
; Should return 5+(3+3) = 11

; One possible way to "subclass"
; Redifine 'double' to always return 0
(define (sub-obj-constructor x)
(with (obj-constructor x) ()
(define (double y) 0)
(this)))
; Invoke add-double on subclassed object
(define sub-obj-6 (sub-obj-constructor 6))
(with sub-obj-6 () (add-double 11))
; Should return 6+0 = 6

; We can polymorph values as well as functions
(define obj-3 (with obj-5 ((x 3)) (this)))
; The value for 'x' in the obj-3 closure is now 3
(with obj-3 () (add-double 4))
; Should return 3+(4+4) = 11

; Totally screw with polymorphism by redefining add
(define obj-5-bad-add (with obj-5 ((+ *)) (this)))
(with obj-5-bad-add () (add-double 2))
; Should return 5*(2*2) = 20

Now that I step back and take a look at this, I think I may stand corrected. It doesn't seem strange at all, even though the code is completely stateless. Whenever you see a (with ...) construct in the code, it's a very strong indicator that the expressions will be evaluated in a different way, and thus you wouldn't be surprised by it. Even the case where I redefined the '+' operator seemed obvious.

In the end, I guess that I can't say why functional languages don't implement OOP more often, aside from the extra complexity of working with first-class closures.

Flat View: This topic has 30 replies on 3 pages [ 1  2  3 | » ]
Topic: Namespacey Programming Previous Topic   Next Topic Topic: Puppets Do JRuby

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use