Dazed and Confuzzled


Alright, Alright, Alright

For a long time I’ve been on a mission to find the lost parts of object-oriented programming. There is clear evidence that somewhere between its inception and modern programming OOP has lost its definition. Dr. David West says in his book, Object Thinking, “An argument can be made that the contemporary mainstream understanding of objects is but a pale shadow of the original idea. Further, it can be argued that the mainstream understanding of objects is, in practice, antithetical to the original intent.”[1] There is an original form of OOP that promises many benefits, with the foremost benefits being system composability and flexibility. The form of OOP that many use today, and the form being taught today is a diluted version. We typically defer to a structured style that places control procedures in direct command of other procedures and use objects as data structures.

I’ve been using a chess program as a project to experiment with object-oriented design. In a previous post, I wrote about problems getting this project off the ground and my personal introduction to TDD. I now have 17 failed versions of the program on my computer (those are just the ones I didn’t delete).

I recently started the chess project again, but this time with some help from a person I admire (James Ladd). I was inspired to reach out to him after reading his blog post on “East Oriented” code. He accepted my request for help, and is providing me with guidance on the project. I’m thankful for his help and patience.

Our conversation so far has some interesting insights. If you’re an intermediate programmer like me you might find you’re asking yourself some of the same questions I’ve asked James. If you’re an advanced programmer, well la-de-da, take a look at the project anyway, smarty pants. If you’re a beginner programmer, you can only aspire to one day feel as confused as I do right now. You can follow the project at github.com/TheSecretSquad/chess. We’re posting our conversation as well as the code so readers can learn from the process.

In his blog post, James describes a pattern of designing object-oriented code with a focus on objects interacting through messages that show intent. James states, “The rigorous application of this East principle decreases coupling and the amount of code that needs to be written, whilst increasing the clarity, cohesion, flexibility, reuse and testability of that code. It is easier to create a good design, structure and identify dependencies and abstractions by simply orienting East.”[2]

So why is East Oriented code an important idea? I think it’s important for at least the following reasons:

  • Instead of asking objects for contextless data on which to operate, we have methods that show intent. Methods show what we intend to do with some data without actually having to hold that data. The data is virtual, an idea, which means it can exist in any form (or all forms… it’s “Schrödinger’s data”…). We don’t need to know what it looks like.
  • It creates objects that interact with smaller, more focused interfaces, which create opportunities for extension without modifying existing code.
  • It localizes changes in the code to the neighborhood of objects that focus on a particular task instead of requiring changes to client code that uses that neighborhood of objects.

All of these things promote key aspects of OOP like composability and flexibility (or “malleability”[1] as Dr. West says) and that’s why I felt compelled to pursue a concept that so naturally fits these ideas.

Who Put the Code All The Way Out Here in the Woods?

Everything we say in code is significant, so we should write code deliberately. The users of an API expect the programmer designed the API deliberately. If a programmer writes a method in a particular way then a user expects they wrote it that way purposely, and not by coincidence or carelessness.

Looking at James’ blog post, he uses an example of a MovieLister class that can list movies by certain attributes (in this case by director). Example 8 shows the original “West Oriented” moviesDirectedBy method. It looks like this:

public Movie[] moviesDirectedBy(String arg)

If we are a user of this API we must assume that the Movie[] return type is significant. We must assume it is important that we get specifically an array of Movie objects. Why an array and not a Collection<Movie>, List<Movie>, Set<Movie>, IEnumerable<Movie>, etc.? The programmer must have chosen carefully what type to return and deliberately chose an array. Or… The programmer thinking in “West Oriented” fashion decided that we need control of “a bunch of Movie objects” so we can “do stuff to Movie objects”.

This doesn’t seem deliberate. Choosing a return type arbitrarily means users are relying on a careless choice that is likely to change at some point. This also promotes procedural style code:

Procedural: “Get the movies directed by a director, then do some action to them.”

Object-Oriented: “Perform some action with the movies directed by a director.”

James’ East version looks like this:

public void applyToTheMoviesDirectedBy(final MovieAction movieAction, final String director)

Looking further at James’ post, pushing it East allows us to hide the movies collection, and do an internal iteration handing each movie off to a MovieAction object. The caller need not care what collection type stores the movies while they’re “doing stuff to the movies”.

I am a little suspicious of any return values. Do we really need that int, or that double, or whatever it is? Do we really need the data explicitly in front of us to work with it?

The Message, as a Significant Preamble to Something Else

Dr. West says about object-oriented programming, “Distributed cooperation and communication must replace hierarchical centralized control as an organizational paradigm.”[1] I believe that transitioning from our current understanding and usage of OOP to a point where we can leverage its intended benefits comes with understanding messages.

I’ve read a lot lately about the concept of messages. I’m not a Smalltalk programmer (yet), but I know the Smalltalk community is known for its focus on this concept. The idea behind messages is objects send messages and neighboring (or collaborating) objects receive those messages. The receiving objects respond to messages by changing their state or sending other messages, which their collaborators receive, and so on. Objects don’t know who is responding to the message, and in many cases they don’t know what will happen in response. They just know to send the message and trust the object hooked up to it at run time responds with the correct behavior.

The thing about messaging is data becomes irrelevant. Data is hidden within objects, and used to store the state of objects. Objects use this state to decide which messages they should send. Objects can’t ask collaborators for their state or data and operate on it. The only way objects release state is by sending a message signaling a significant event. Other objects can register to receive the message and respond to it. This type of design naturally leads to East Oriented code because you’re not returning data (west).

Kent Beck and Ward Cunningham said, “The most difficult problem in teaching object-oriented programming is getting the learner to give up the global knowledge of control that is possible with procedural programs, and rely on the local knowledge of objects to accomplish their tasks.”[3] Getting messaging right is hard, and I’m only recently finding how the concept differs from methods. Methods implement messages. In many languages methods resemble procedures or functions, so most people (including me) just end up writing classes that are modules with procedures.

In trying to apply this messaging concept to my code I find myself stuck once again. What should these messages look like? The basis for my analysis is:

  1. An object should send messages to client-specific interfaces. This means an object should send messages that are meaningful to itself, and interfacing objects should have only those messages available.
  2. The messages should focus on what not how.

In both cases we are attempting to avoid unneeded context. In the first case an object wants awareness only of the messages it needs to know about. In the second case we want to avoid the context of how an object carries out a task and focus only on what the message does.

The second case is particularly interesting to me: What does “what” mean?

I Hear My Name Over Here? You Guys Talking About Me?

I recently read Practical Object-Oriented Design in Ruby by Sandi Metz. It significantly effected how I think about object-oriented code. My understanding from reading Sandi’s book, is that objects are not modules of functions operating on mutual data and state as we are often taught. Maybe that’s what they are to us as programmers, but that’s not what they are to other objects.

The public interface to an object is not “what the object does”. The public interface to an object consists of the messages it responds to; “what an object does” consists of the messages it sends. I think it’s this inversion of thinking that puts the power into objects. Ideally, a message sender should have no idea what will happen when it sends a message.

One topic Sandi wrote about that keeps rolling around my mind is the context objects have when sending messages. When an object sends a message to a collaborator it carries some knowledge or expectation of its environment. In her book she describes the relationships between communicating objects like this:

  • I know what I want and I know how you do it. [(least flexible)]
  • I know what I want and I know what you do. [(somewhat flexible)]
  • I know what I want and I trust you to do your part. [(most flexible)]
    [4]

It’s easiest to understand with the example from her book, but I’ll try to summarize my interpretation:

  • I know what I want and I know how you do it: Calling methods on an object where the method names are semantically part of the called object’s context. This binds the caller to the process implementation, i.e., how the object accomplishes the process. This is basically treating an object as a module with procedures and the caller must know how to use the procedures to accomplish a desired task.
  • I know what I want and I know what you do: Sending messages to an object where the message names are semantically part of the called object’s context. The caller is not bound to an implementation, but is bound to an expectation. The caller expects the receiver to enact some process related to the called object, and that will never change. Receiving objects have no option to do anything else. If the response to the message has to change or have added processes, then the caller must change to accommodate the new messages or changed names.
  • I know what I want and I trust you to do your part: Sending messages to an object where the message names are semantically part of the calling object’s context. The caller is not tied to an implementation, and has no expectation of the called object’s reaction. The programmer expects a specific reaction based on the object hooked in at run time, but the result of the message can differ in both implementation and result.

I don’t know if this is the best explanation, but I tried. It’s probably better with an example. One thing that comes to mind that I can use as a short case study is the “tell, don’t ask” example given by Steve Freeman and Nat Pryce in their Mock Roles, not Objects paper.

They start with the example of a “train wreck” of getters:

dog.getBody().getTail().wag();[5]

This is I know what I want and I know how you do it. An alternative design of the same nature could be:

// Dog being happy
dog.wagTail();
dog.bark();
dog.drool();

They then refactor to “tell, don’t ask” style:

dog.expressHappiness();[5]

This is more like a message than calling procedures on a dog module. Here we tell the dog object what we expect, not how to do it. The caller doesn’t know how that happens; the dog might reference a body object, or it might bark or wag its tail, etc.

This is where they stop the refactoring, but I think it could be better. If we apply Sandi’s ideas, this is I know what I want and I know what you do.

Any caller sending this message, knows to expect a dog will express happiness. That will always be the result. The clients of this message are also responsible for figuring out what makes the dog happy, and then telling the dog to express happiness. We can see that this message takes the perspective of “what the caller expects from an object”, which means the caller is aware of the called object, and must carry it in its context.

Lets say a feature we want is for the dog to show certain emotions based on some actions or events, we would end up writing code logic like this:

if someone throws a ball
dog.expressHappiness()

if loud noise
dog.expressFear()

if owner is gone
dog.expressSadness()

etc.

I can’t even think of what to call the class that figures out what makes a dog express certain emotions. It doesn’t seem like clients should figure out what makes a dog express certain emotions anyway. It’s something the dog should do.

One pattern that stands out to me is if you can predict the outcome of a message, then you can probably abstract it further. For example, when we say dog.expressHappines(), we don’t know in what way the dog will express happiness, but we do know the semantic outcome will be the dog expressing happiness. We have locked ourselves into that. It’s now part of the client’s vocabulary. If one of the main benefits of OOP is flexibility (malleability), then I think we can take this a step further.

So how do we get to I know what I want and I trust you to do your part? I’ve experimented with an idea that I stumbled on while talking with James as part of our chess project (you can see how that went on github).

The idea is to rephrase the problem from the perspective of the caller (or client) like this:

“Y is an example of something Y-object could do when “the client” does X”.

“Expressing happiness (or fear, or sadness) is an example of something a dog could do when someone throws a ball.”

Using this statement we can see that the dog’s behavior is a reaction to a message sent by someone else throwing a ball.

It might look like this in code:

public class Dog {

	public void throwBallTo() {
		this.expressHappiness();
	}
}

We’ve now protected clients from changes to the dog’s expressions. Dog implementations are free to express any emotions when someone throws a ball, like fear, if it’s a nervous dog. A grumpy dog that never expresses happiness doesn’t have an expressHappiness() method hanging around for no reason. Obviously the throwBallTo() method could be generalized to something like throwObjectTo(), or whatever else. The point is that the message sender no longer expects the context of a dog or any other animal that expresses emotions. If throwBallTo() was a method of a separate interface, then the caller doesn’t even need to know about the Dog class. The caller expects it can throw a ball to anything and if it’s a dog it will express the implemented emotions or generally do dog stuff.

Notice the difference compared to before. We could predict the outcome of the method call; expressing happiness declares that in one way or another the dog expresses happiness. Throwing a ball to something guarantees infinitely more possible outcomes from the client’s perspective.

Sandi says, “The best possible situation is for an object to be completely independent of its context. An object that could collaborate with others without knowing who they are or what they do could be reused in novel and unanticipated ways.”[4] I think this type of design change achieves this. I think looking at the messages from the client’s perspective helps.

Fixin’ to be A Lot Better, Man

So how does this relate to the chess code? Here’s the relevant code from chess:

Tests for the Game class:

@RunWith(MockitoJUnitRunner.class)
public class GameTest {

	private Game game;
	private Square square;
	@Mock
	private MovesReceiver movesReceiver;
	@Mock
	private Board board;
	@Mock
	private StartingPieceConfiguration startingPieceConfiguration;

	@Before
	public void setUp() throws Exception {
		square = anySquare();
		game = new Game(board, startingPieceConfiguration, movesReceiver);
	}

	private Square anySquare() {
		return Square.A1;
	}

	@Test
	public void shouldSendMovesForSquareWhenSquareIsChosen() {
		game.chooseSquare(square);
		verify(board).sendMovesForTo(square, movesReceiver);
	}

	@Test
	public void shouldSetupPiecesOnTheBoardWhenStarted() {
		game.start();
		verify(startingPieceConfiguration).setup(board);
	}
}

The Game class:

public class Game {

	private final Board board;
	private final MovesReceiver movesReceiver;
	private StartingPieceConfiguration startingPieceConfiguration;

	public Game(final Board board, final StartingPieceConfiguration startingPieceConfiguration, MovesReceiver movesReceiver) {
		this.board = board;
		this.movesReceiver = movesReceiver;
		this.startingPieceConfiguration = startingPieceConfiguration;
	}

	public void chooseSquare(final Square square) {
		board.sendMovesForTo(square, movesReceiver);
	}

	public void start() {
		startingPieceConfiguration.setup(board);
	}
}

The Board interface:

public interface Board {

	void sendMovesForTo(Square square, MovesReceiver movesReceiver);
}

The StartingPieceConfiguration interface:

public interface StartingPieceConfiguration {

	void setup(final Board board);
}

The gist of what’s shown here is when someone chooses a square, the Game responds by sending the available moves for that square on the board to an object that receives the moves. The receiver of moves most likely will display them in some way, but could do anything it wants with the moves. When someone starts the game, the game sets up starting piece configuration on the board.

I think the code and tests model the domain well and it’s all clear as far as code comprehension. Still, something about this causes conflict in my squishy brain parts.

The question I have, is should the Game class send the message sendMovesForTo(square, movesReceiver) to the board? This is a feature that I wanted to implement, and I think we did a good job of abstracting it (you can see how on github), but I can’t help but look at this as I know what I want and I know what you do. If the point of using OOP is its flexibility, then we should try to keep it maximally flexible, so in my mind the goal is code that embodies I know what I want and I trust you to do your part, and I should seek to change this code.

If someone else wanted to use the Game class, but had no interest in sending the moves anywhere, they are still dependent on an interface that has that message. Their Board implementation needs to make sure that it sends the moves to a receiver even if they don’t care about this.

The Game knows about something that is holding the pieces on a grid of squares, i.e., the board. What message can the Game send that the Board could respond to with our expected result, and still supports many possible outcomes?

Looking at it from the client’s perspective:

“Sending the moves for a square to a receiver is an example of something that could happen when a square is chosen on the board.”

I thought of changing the code to:

public void chooseSquare(final Square square) {
	board.chooseSquare(square);
}

It seemed pointless at first to call the same message on Board that we call on Game; what does this accomplish? Is this just frivolous indirection. Then I thought that maybe choosing a square in the context of the game is different from choosing a square in the context of the board. These objects have different opinions on what happens in response to the chooseSquare message. It’s the Game‘s job to invoke the behavior on the Board.

With this change the MovesReceiver would be removed from the Game class and be passed as a constructor argument to a class implementing the Board interface.

I made similar changes to “setting up the pieces on the board”. I changed the test to:

@Test
public void shouldPrepareTheBoardWhenStarted() {
	game.start();
	verify(board).prepareToPlay();
}

And the Game to:

public void start() {
	board.prepareToPlay();
}

The reasoning is “Setting up the pieces on the board is an example of something that could happen when the game is preparing to play.” Maybe the board does other things during preparation. If the game knows about one of those things, then Game would have to know about all of those things when we inevitably add to the list of things the board does during preparation.

I can see how the prepareToPlay() method might create an opportunity to have other objects that are part of the Game‘s preparation and respond to the message. However, this is not something I would worry about now. I’m just examining the possible effects these changes have on the design.

Despite everything I just said, and all the research and evidence I have for making my point. I’m still not convinced of any of this. I don’t know if these changes are better than before.

Wait, Why Are You Smiling?

Lastly, I noticed these changes affect the tests. I can no longer write a Game test that guarantees that we send the moves to a receiver, which is the feature I wanted in the first place.

Instead my test would have to read something like this:

@Test
public void shouldChooseSquareOnTheBoardWhenSquareIsChosen() {
	game.chooseSquare(square);
	verify(board).chooseSquare(square);
}

At this point, to make sure the feature exists I would have to begin implementing a class that implements the Board interface, and test that it implements the “sending moves” feature. I’m relatively new to unit testing, so I’m not sure what to do here. Do I stop writing/testing the Game class and switch to implementing this feature in another class? Do I hang up the feature for now (put a note in Trello or something) and worry about it later? Is this a sign that I’ve abstracted too much and should instead revert my changes? I don’t know.

There Was Just a Little Bit of Bullshit in All That Right?

That’s all I have to say for now. I still have an inkling that everything I just said is complete nonsense.


[1] West, David (2004-02-11). Object Thinking (Developer Reference) Pearson Education. Kindle Edition.

[2] Ladd, James. “DRAFT – A Design Compass: East Oriented …” JamesLaddCode. JamesLaddCode, Feb 2007. Web. 13 Oct. 2014.

[3] Beck, Kent, and Ward Cunningham. “A Laboratory For Teaching Object-Oriented Thinking: From the OOPSLA’89 Conference Proceedings October 1-6, 1989, New Orleans, Louisiana.”SIGPLAN Notices 24.10 (1989).

[4] Metz, Sandi. Practical Object-Oriented Design in Ruby. Pearson Education, 2013.

[5] Freeman, Steve, Nat Pryce, Tim Mackinnon, and Joe Walnes. “Mock Roles, Not Objects.”OOPSLA (2004).

Blogs and Other Sources I’ve Been Studying While Thinking About All This

17 thoughts on “Dazed and Confuzzled

    • Hey Pawel, thanks for reading. Those links look interesting. I’m always interested in different perspectives on this subject. Looking forward to checking them out. Thanks for sharing.

  1. Hi Pete, thank you for this interesting and thought-provoking article. I consider myself to be an intermediate developer, in terms of experience, but rather lower when in comes to aptitude. I constantly look at my code and think, ‘there has to be a better way’.

    A common problem (for me) is the need to add a record to a data store and, if successful, take some other action like create a folder on a network for storing documents related to the record.

    Problem one is identifying if adding the record was successful. Using the ‘East’ approach, how would the calling method know if the record had been created? Perhaps it shouldn’t know and that’s the point? But something needs to know in order to provide feedback to the client and take (in my example) some other action.

    • Hi there. Thanks for reading. Don’t worry about experience or aptitude. Both come with learning and doing, so just keep learning and doing 🙂 I’m still learning and doing myself, and don’t consider myself a necessarily apt programmer. I haven’t written any of my blog posts because I know things. I write them to explore ideas I don’t really understand.

      I too, constantly look at my code and think, “there has to be a better way”. I don’t have a solution to instantly build confidence in your code and stop thinking that. I wish I did for my own sake. I do however think the people who are thoughtful enough to examine their code and think, “there has to be a better way”, are the people who eventually write better code (with lots of learning and doing, of course). There are those who never give their code a second thought as long as it works, and are doomed to producing expensive technical debt.

      In regard to adding a record to a data store and checking success… There are lots of ways I can think to do it. As long as the code is neat, understandable, tested, and free from unexpected behavior, I don’t think there is any way you can go wrong.

      You can use anonymous functions as continuations, to continue processing on success or failure:

      datastore.addRecord(record: aRecord, onSuccessDo: () -> successAction(), onFailDo: (error) -> failAction(error));
      

      You can do something similar and bundle the success or failure into one object:

      datastore.addRecord(record: aRecord, resultListener: aResultListener);
      

      where you have an interface like:

      interface AddRecordResultListener {
        onSuccess();
        onFail(Error error);
      }
      

      You can return an object containing the result.

      AddRecordResult result = dataStore.addRecord(record: aRecord);
      

      You can treat the result different ways as well:

      if(result.success()) {
        successAction();
      }
      else if(result.failure()) {
        failAction(result.error());
      }
      

      or:

      result.ifSuccessIfFail(onSuccessDo: () -> successAction(), onFailDo: (error) -> failAction(error));
      

      You might even find that different clients of the code work best with different usages and choose to implement different data store interfaces for different clients.

      // Bad interface names here, but it’s just an example

      interface DataStore1 {
      void addRecord(Record record, Runnable onSuccess, Consumer onFail);
      }
      
      interface DataStore2  {
      void addRecord(Record record, AddRecordResultListener listener);
      }
      
      interface DataStore3  {
      AddRecordResult addRecord(Record record);
      }
      
      class MyDataBase implements DataStore1, DataStore2, DataStore3 {
       // etc...
      }
      
      MyDataBase myDatabase = new MyDataBase();
      client1DoThing(myDatabase);
      client2DoThing(myDatabase);
      client3DoThing(myDatabase);
      

      It depends on what role you’re filling for the calling object. You can write the code that works for the client and then choose an object to implement it.

      Thanks again for your question. I hope I’ve helped. I’m here if you’d like to discuss further.

  2. I’m looking for a better way to produce maintainable code for years and, so far, there are two things I’m guessing to be better:
    No return values, like the East Oriented Programming you referred to and PubSub or Microservices (at least using RabbitMQ).

    Why I suppose that return values are bad? Because as you said, you lose flexibility, because you assume that specific type of object, and can’t change it later easily.

    I also like to think of return values as a steal, because of the client object “coarse” the other object to give back him something and just that moment, I mean, you can’t postpone the result, whilst when you call a method without return, you don’t know even when things will happen.

    This is even more evident when programming Microservices using RabbitMQ because the “objects” send messages but they don’t even know to whom. The object that “wishes” to respond to that message just subscribe to that, if it thinks it can do the job, without knowing the sender.

    I consider that when you use return values, you are treating objects like functions, resembling function programming. I suppose that when Smalltalk was created (if I’m not wrong) it was inspired by Self and Lisp, and Lisp is mostly a functional programming language.

    I suppose that OOAD should be the nearest possible to the problem you’re trying to solve, because, if the problem changes, the code changes the same way.

    Maybe I’m completely wrong thinking this way, but I’ll continue studying and philosophizing.

    Any idea or criticism is welcome.

    • Hi Bruno. Thanks for reading and for giving your feedback. I’m sorry I’m so late replying to your comment.

      I think microservices and pubsub are object-oriented. The interesting (funny) thing is the rise in people talking about how object-oriented programming has failed, yet praise microservices. I believe the use of microservices is object-orientation applied to the system architecture, instead of just the source code or component level.

      In East Oriented Programming, James says factory objects are allowed to return objects, but that makes sense because you need some way to create objects. Otherwise, suspicious return values typically appear when objects expose data instead of behavior. Like you said, you lose flexibility with return values because the client needs to understand the protocols of this new thing that was returned. If that returned thing changes, the client needs to understand new protocols.

      I think a return value can be used to “postpone” a result. Think of “Promise” objects often seen in Javascript. However, I often see promises used when acquiring resources, like requesting an object from an API. In that case it seems like a factory to me. But in theory you can have a method return a “promise-like” object that can be queried for a result. For example,

      public class Something {
          public MyPropertyResult myProperty() {
              return new MyPropertyResult(...);
          }
      }
      
      Something s = new Something();
      MyPropertyResult r = s.myProperty();
      r.withValueDo(value -> { /* do something with result value */});
      // withValueDo could (I think) even wait for an async process to finish before activating the callback
      

      I don’t know if the above is a pattern I would use myself, but I guess it’s a possible solution to something.

      This is even more evident when programming Microservices using RabbitMQ because the “objects” send messages but they don’t even know to whom. The object that “wishes” to respond to that message just subscribe to that, if it thinks it can do the job, without knowing the sender.

      This really is the essense of object-orientation. Using something like RabbitMQ makes me think of the Actor Model (https://www.brianstorti.com/the-actor-model/), which is similar to object-oriented programming.

      I consider that when you use return values, you are treating objects like functions, resembling function programming. I suppose that when Smalltalk was created (if I’m not wrong) it was inspired by Self and Lisp, and Lisp is mostly a functional programming language.

      Yes, functional programming relies on immutable data and functions that return values. We can write functional-style code using object-oriented languages and that is ok, because the intent is to make a functional programming interface. Numbers and Strings are typically “functional” in the way they behave in object-oriented systems (they are immutable and their operations are pure functions and return new values). Often, object-oriented code is written with data objects that are mutable, which is where I think things get messy.

      I think it’s good to use functional style within object implementations, but higher-level objects are often mutable because they hold the system’s state, and they do better communicating through higher-level messages that incidentally modify state, because…well… state has to change somewhere in order for anything to happen. The goal of East, is to hide troublesome data and state in mutable objects and enforce higher-level protocols between these higher-level modules, while the hidden, lower-level code can be functional if it suits the solution.

      Smalltalk was inspired by Lisp and Simula (and influenced Self). But Smalltalk was also inspired by cellular biology (Alan Kay studied Biology in college) and early Internet technology (Alan Kay was involved with ARPANET). Smalltalk and Alan Kay’s definition of objects are a simulation of the behavior of networked computers. The same kind of abstraction (in OO, it’s objects, in network it’s computers) have their own hidden data and state, and can only communicate with each other using messages defined in agreed-upon protocols. The Internet is an object-oriented system. As far as I know (with my limited networking knowledge), all the protocols in the network protocol stack facilitate communication using messages, whether it’s TCP/IP or HTTP. Regarding cellular biology, Alan Kay was inspired by how simple cells could work independently, and manifest a system that is more than the sum of its parts by messaging each other.

      I suppose that OOAD should be the nearest possible to the problem you’re trying to solve, because, if the problem changes, the code changes the same way.

      This is definitely a conclusion I’ve come to also. The closer your code maps to the problem you’re solving, the less difficulty you have changing things.

      “Maybe I’m completely wrong thinking this way, but I’ll continue studying and philosophizing.”

      I’m by no means an expert, as I’m still figuring this stuff out myself, but based on my experience I think you are on the right track.

      Thanks again. Feel free to discuss further.

      P.S. – I’ll respond to your other comment soon.

  3. I forgot to make a question: How should a “pure non-returning object oriented like programming language” be?

    For example, the implementation of the numbers and strings (considering the “I know what I want and I trust you to do your part” theory).

    • This is a good question. I’m honestly not sure, but it’s interesting to think about what a non-returning language would look like. I don’t know if it would be practical. I think we need to be able to return certain things. The thing about return values is that code that indiscriminately returns object properties, fails to co-locate data and behavior. Whenever I see return values it makes me think twice whether the object really needs to return something or whether the work being performed with the return value can be done by the object.

      I’ve never designed a language, and I’m dangerously unqualified to do so. I could take a guess that the semantics of a language that didn’t have returns would depend on continuation-passing style. Most methods would take extra arguments to receive results.

      someObject.doThing(input, (output) -> doWithOutput(output));
      

      Having immutable values return altered versions of themselves is the only exception I would expect. So Strings and Numbers would be functional style values that produce new versions of themselves when changed. Only because the alternative might be a bit messy and counter to normal expectations.

      // Functional style
      // I don't consider this "returning", because the result is the same type of object,
      // meaning there is no revealing of an internal object graph.
      String y = x.trim().replace('a', 'z');
      

      I think in the case of functional values like String and Number, “I know what I want and I trust you to do your part” is safely enforced. We’re dealing with an abstraction of a String, we can ask it to trim itself and trust that we then have a string that is trimmed.

      // Continuation style
      Consumer<String> action = (stringValue) -> System.out.println(stringValue);
      x.trim(trimmed -> trimmed.replace('a', 'z', replaced -> action(replace));
      

      I don’t know if the above continuation style adds much to the abstraction.

      We might be able to make other methods non-returning, like Java’s String[] split(String regex) method?

      Maybe we could use continuation-passing here…

      // Calls the consumer with each result from the split
      void split(String regex, Consumer<String> result)
      
      // Calls the consumer with each result from the split and its index
      void split(String regex, BiConsumer<String, int> result)
      

      Now we don’t have to deal with a new array abstraction. Then again, we lose some fidelity here, because the array has things like a count of how many elements there are. This might need a new solution, maybe

      void split(String regex, Consumer<SplitString> result)
      

      But I often find methods on functional objects that produce an alteration on the object are better returned, so for example

      SplitString split(String regex)
      

      might be better to hide the array, but then SplitString could have methods like SplitString::each(Consumer<string> action)

      Smalltalk’s Number>>to:do: message is an interesting example. Instead of having a looping construct, you can send a message to a number to loop until a stop value, executing a function each time.

      1 to: 10 do: [:value | Transcript show: value]
      

      I would have to give this a lot more thought. We could always try taking a class from another language and see what it’s like to convert the entire thing into a style that doesn’t return anything. It could be a fun experiment.

      Maybe if I ever get smart enough to make my own language, I’ll try to minimize returns.

      What do you think? Do you have any ideas about what a language might look like that has no return?

      • Thank you for the answers, Pete.

        Yes, this “higher order functions” approach sounds good.
        I also started to study Erlang based on that Actor Model reference you provided, thank you so much.

        I was thinking of a “message oriented programming language” once. But I didn’t dived into that, but let me share the idea (feel free to comment, give suggestions, ask questions and whatnot):

        // The symbol means “send/publish”, since it’s going to be used a lot, I think some short symbol like this would be nice

        a_module {

        <- a_message[string_a, string_b] {
        
            // suppose string_a = "First", string_b = "Second"
        
            // Pattern matching (so this message is gonna be called only when receives "string_a", "string_b", and something else ("result" is like an atom in Erlang)
        
            <- appended[string_a, string_b, result] {
        
            }
        
            -> append[string_a, string_b];
        }
        

        }

        // Since we have “temporal decoupling” (for example, a module can subscribe/listen to another message right after receiving another message, those sort of message queues might be necessary

        another_module {
        <- another_message[] {

        }
        
        // this module is gonna listen to the same message as "a_module", because of "pattern matching"
        <- appended["First", "Second", result] {
        
        }
        

        }

        builtin string_module {

         // looks like procedural syntax, but since we don't reference "objects", we can't do something like first_string.append(second_string)
        <- append[first_string, second_string] {
            ...
            -> appended[first_string, second_string, result];
        }
        

        }

        There might be lots of flaws, but anyway, what are your thoughts? Just toying around with ideas.

Leave a reply to Pete Cancel reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.