6

How can Gary Bernhardt's "Functional Core / Imperative Shell" architecture be used to design software for an elevator system?

Specifically, let's say there are a few elevators, each with call buttons (one per floor). Each elevator has sensors that report speed, position, and button status, and a controller that accepts instructions such as go up, go down, stop. Also each floor has sensors that report the status of the up and down call buttons.

Pick whatever is easiest for IO, like reliable messages or whatever. And pick whatever you is best in terms of concurrency (multiprocessing, multithreading or just asynchronous code).

An advanced elevator system bases its decisions on more than just current position, speed of each elevator and the state of request buttons on the floors and inside the elevator. In particular, to provide instructions to the elevators, it would need to know how long ago various events happened (to improve fairness) and perhaps even what the recent history was (this would help predict future requests).

But providing so much information as an input to a pure function seems very messy.

Am I thinking about it wrong, or is this problem just not well suited to that architecture?

Edit: as @ThomasKilian pointed out, the talk I'm referring to may not be as well known as I thought. So let me expand the question: I'm interested in any approach where all the non trivial business logic is mostly coded using functional programming style.

1
  • I guess you address only a very limited number of persons being familiar with this talk.
    – user188153
    Commented Nov 18, 2016 at 23:45

1 Answer 1

2

If you encapsulate your data into objects, and decompose your functions suitably, I think you could easily use the functional core approach. Imagine a Floor object to keep track of the buttons pressed on each floor, an Elevator object that keeps track of the speed and buttons inside the elevator. For keeping track of the history, you could take advantage of immutability and push states onto a stack instead of replacing them. The interaction between the shell and core could look something like:

List<Stack<Elevator>> elevators;
List<Stack<Floor>> floors;

while(true) {
  orders = core(elevators, floors);

  runOrders(orders);

  foreach (elevator in elevators) {
    elevator.push(getNewState(elevator.peek()));
  }

  foreach (floor in floors) {
    floor.push(getNewState(floor.peek()));
  }
}

The imperative code passes the orders on to the real-life elevators, retrieves their new states, and pushes those states onto the stacks. Inside the core, you don't need anything non-functional.

Yes it's a lot of data to pass around, but that's because you require a lot of data. Any code to manage positions, speeds, buttons, histories, etc. is going to be complicated. Do you have any reason to think an imperative implementation would be simpler than a referentially-transparent functional one?

3
  • 1
    +1 It's a complex system and the dependencies are always there, whether implicit or explicit. The functional approach just makes them more explicit.
    – Andres F.
    Commented Nov 19, 2016 at 3:43
  • What if the core needs to just know the delta from previous state to do its job, but the amount of state information is large so calculating delta is expensive and/or messy code-wise? Wouldn't it be more attractive for the core to just ask for messages that inform it of any changes - leading, unfortunately, to stateful core?
    – max
    Commented Nov 22, 2016 at 16:54
  • If all it needs to know are the changes, then you could have a Changeset class to encapsulate the relevant changes and an applyChanges(State, Changeset) => State function that doesn't need any mutation. Instead of having the core ask for messages (pulling information), have the shell send messages (pushing information) when the real-world state changes. Again, if calculating the delta is expensive and/or messy, it will be problematic regardless if it is in a functional or imperative style.
    – Andrew
    Commented Nov 22, 2016 at 18:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.