Functional single argument style object oriented programming

So I had this thought as regards a programming language. Basically it would a lot like forth, only instead of a stack there would be a default initial object. You get new objects by sending messages to this initial object. Things like numbers would actually be messages that would result in the receiver producing a new object to act like a number. The only thing you could write would be messages. Messages would be represented in the source file as whitespace separated strings, much like forth. Other than that there would be no syntax. Basically, I want you guys who all know a lot more than me to point out the flaws in this idea. Heres some examples of what I envision:

1 + 2

This would break down into:

1

Send the message "1" to the default object, this will result in a new object being returned that results in the integer 1.

1 +

This sends the "+" message to the 1 object, which returns a new object an "adder".

1 + 2

This sends the "2" message to the "1 +" adder object. this results in a new object, a three.

As you can see, messages are evaluated from left to right, and there are no arguments. A program in this language could almost be viewed as one giant single argument function.

Unlike FORTH, you don't have to use post-fix notation (although there is no operator precedence, just as in Smalltalk).

Due to the syntactic baggage of a class based object system I would suggest this be implemented as a prototyped system.

A clone message could be used to well clone an object. You could then add methods to it by sending it another message:

clone define-response hello string hello display end-define-response hello

I have named my method definition method "define-response" since messages are pretty much the only thing you got, and your more responding to messages than invoking methods. Of course if the above is confusing it can of course be written as (I will borrow FORTHs commenting convention here):

clone ( Create a new object )
define-response hello ( define the hello method )
    string hello ( Create a string with the contents "hello" )
        display    ( Display that string and return it )
end-define-response ( Finish the method definition and return the object )

hello ( Call the hello method of the newly created object )

Note that define-response creates an object that sets up some internal state. It receives its first message, it responds by naming the method for the object it is creating after the message it just receives. It then returns a new object that simply consumes and stores the messages it receives until it receives a message of end-define-response When it receives this response it creates the method in the object it was defining it for and returns that same object. You can now call additional methods on it, including defining more methods. You can see how any control structure can be implemented thru a combination of this kind of object and/or recursion. Nesting can be handling by having these objects maintain an internal stack, they need only recognize an invocation of themselves, any other nesting problems can be detected when the message is responded to.
A major problem with this system is variables. Of course I can just add some methods and change them as I need to change the variable e.g.:

define-response  x 1 end-response
x display ( Prints 1 )

But that's verbose and creates the equivalent of global variables. It also has performance issues since x has to be evaluated every time it is called, this creates problems especially if the object created is complex. So I propose a slight modification to the define-response method. The creation of a define-response-immediate method. The only difference between the two would be that the body of a define immediate response would only be evaluated once, when the method was defined. Every other time the message was sent it would return the cached value. The rest of the issues with the verboseness of variables could be handled with additional methods, like copy_to e.g.

1 + 2 copy_to x

It uses this "backwards assignment, since I'm trying to avoid parentheses, although they could be added.

I look forward to any criticism.

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

You run into the same problem as Smalltalk

How would you interpret the expression:

1 + 2 * 3

In Smalltalk, which is similar to what you are proposing, the answer would be 9, since it's left to right with no operator precedence (1+2)*3. Nothing wrong with that, other than it goes against the conventions that multiplication has a higher precedence. Still, it's one of the first design decisions you'll have to make.

Problem? Its a question of perspective :-)

Operator precedence is confusing. I can't remember all the rules, so I use parens - regardless of language.

I agree though, the language he envisions is quite a lot like Smalltalk (except for that original object from whence all objects flow - that would be the compiler).

At least it looks like Smalltalk until he gets to the idea of prototypes and cloning - then it looks like Self.

Prototyping

I suggested prototyping since I imagine having classes would require variables. (If nothing else to hold the name of the classes, especially in cases involving inheritance). Also I kinda like prototypes as a general anyway.

I think I mentioned this

That there would be no precedence as in Smalltalk

That's what I get for skimming too quickly. :-)

From a general viewpoint, I'm not sure what you are trying to achieve? Is there some flexibility of Forth that is missing in Smalltalk/Self that you are striving for? Or is this an exercise in trying to build a minimal core/kernel - allowing you to be as minimal as Forth?

I dont think i understand

I dont think i understand how clone works. What object is it cloning and where does the clone go?

similarity

This's similar to the language that I'm just starting to write (I'm calling it Mimsi, but don't google for it for a while, since it's a spare-time project), where all functions are compiled to single-argument messages on objects (messages including curried parms). Doing it explicitly is way hard on the programmer, though: I think that should be done in the compiler instead, after all the operator de-macro-ifying etc. has been done.

Computational model

Interesting ideas. Some of what you describe sounds a lot like the Actor model, but with a twist. However, it's not clear (to me at least) from your description how your systems actually works. For example, how is the object/actor that is receiving a message identified? From your description, it looks like the receiving object is whatever object is at the top of some kind of "object stack". Is that correct? If so, how do objects get popped off of the stack, and what happens to them when they get popped. If not, how are objects addressed? Can objects hold references to other objects? What does "clone" actually do (I'm guessing it's a message to the "current object" -- i.e. the top of the stack)? Where does the object returned by "clone" go (from your example, it appears to be the top of the "object stack")?

I guess what I'm suggesting is that you step back from the syntactic concerns for a bit, and figure out how the computational model works. This is not to say that your computational model can't be influenced by your desired syntax. But you need to figure out exactly what you want to be able to say before you can really figure how it should be expressed syntactically.

The what

Actually there is no object stack. There is more of a single "accumlator" register. The result of each message replaces the current object. It just happens to start off with an objec that responds to a lot of messages in the "accumulator". I believe the syntax has been very much influenced by my model, maybe I haven't been speaking clearly. Basically the model is that of objects receiving messages. The message is simply the name of the message, there's no concept of formal arguments or anything like that. One could say the object is a function and the message its single argument. It's kind of the lambda calculus from an O-O point of view. Am I being clear?

Clarity

Sorry. It seems that I misunderstood what you had written. Which is why I asked the questions that I did. Let me ask some more questions, and see if I can achieve some clarity of understanding wrt what you are proposing.

I understand that your basic model is "objects receiving messages" (which is why I drew a comparison with the Actor model). What I'm having difficulty with is figuring out how you manage message distribution within your model. You say

The result of each message replaces the current object [emphasis mine]

Objects appear (from what I have seen so far) to be anonymous. So which object receives a new message? Is it the one currently in the "accumulator"? Or is there some mechanism to distribute messages to objects other than the one in the "accumulator"? If the latter, what is so special about the "accumulator"? That's what I'm not seeing right now.

I'm also having difficulty understanding the mechanics of the object life cycle. You say that

It's kind of the lambda calculus from an O-O point of view
But (to me at least) objects imply persistence of state (otherwise you might as well just use pure functions). So where do all of the other objects go when they get replaced in the "accumulator"? And how do they get back into the "accumulator" (assuming there's no way to send messages to objects not in the "accumulator")? If they don't ever get back into the "accumulator", then what's the point of persisting state? Perhaps I'm just stupid, but as with the message distribution scheme, I'm having trouble seeing how this works based on the descriptions and examples you've given so far.

Ok, so here's what happens

When the compiler / system / interpreter gets started up, it places an initial object in the accumulator. (I use accumulator in the machine register sense). The result of a message always gets placed into the accumulator, so the next message gets sent to the result of the previous message ( every().method().call().is().chained() ). The initial object is special, it's the root in the inheritance tree. Because of this, all objects can always get back to it (since it is their prototype, and they have a "super" message or something similiar, I'm considering calling it "object"). e.g.:

1 + 2 # make a three object
display # print the three
object # get the root object back in the accumulator
2 + 7 display # print the number 9

An alternative syntax:

accum = new Object;
accum.theNumberOne().Plus().theNumberTwo().display().rootObject().theNumberTwo().Plus().theNumberSeven().display()

Thank you

That clarifies things quite a bit.

Confused

I'm sorry, but this looks like a sugared version of pure untyped lambda calculus. It would be exactly that if you started out as "+ 1 2" (is "+ 1 2" the same as "1 + 2" in this language?). The difference seems to be that a value needs to be encoded such that it applies itself to a binary operator, its argument.

Io and concatenative languages

What you propose looks similar to the language Io (prototype based, inspired by the Actor model amongst others).

It also fits into the field of so-called concatenative languages.

You could check out the posts on ltu about

  • Io (Steve Dekorte)
  • Joy (Manfred Von Thun)
  • Factor (Slava Pestov)
  • XY (Stevan Apter)

The concatenative Yahoo! group should also provide very interesting reading (e.g.).

so you're implicitly

so you're implicitly constructing a tree of objects and you can access members via the dotted notation, starting from object?

it sounds like your next problem is how to have some kind of relative addressing. for example, how do you process all children of a node? how do you refer to "the siblings of"?

i like the idea.

how does garbage collection ever work, though?

Assignment

Your "copy_to" assignment "shortcut" still has a problem. You already said it's not easy. In your example

1 + 2 copy_to x

it is not clear which object's x is meant. So this is not an appropriate shortcut for the more verbose define-response-immediate definition. So you'd have to do something like

1 + 2 copy_to obj x

However, as I understand your proposal, "obj" will be treated as a literal message, but all objects you create appear to be anonymous so you can't just treat "obj" as a message name (this would also be restrictive as you couldn't calculate the target object dynamically) but need to evalute it. This may "break" the purity of your concept as the evaluation will result in an object and not a message name which is being applied to the object copy_to returns (as I understand, messages are simply strings and not objects). Which may be a general problem if you'd choose to introduce parentheses, so you might have to define some object -> string conversion which is implicitely applied to the object to get the name of the message to invoke.

As for parentheses, I think that some rather simple expressions will be tricky to write in your language without parentheses, or at least defining some special "." dot message which has a higher priority. The latter would require a lookahead and be less general than parentheses as no nesting is possible, but it still might look "cleaner" than parentheses if that is your concern.

Yeah

Well we have two choices. It's either the root object, or it's 3. Since storing something inside itself is often redundant, I imagine this would define a global x to be three. I admit this language isn't very practical. As far as having "member variables", you could write it the long way, or I could just have a block setter form:

some-object set-instance-var x 1 + 2 end-set-instance-var