Skip to Content

In this post, I think about how we program, about the way we describe what it is that we want the computer to do for us, and look at what has become for me the canonical example of the difference between ‘what’ and ‘how’.

Over the past week, three worlds have collided, in a most pleasing way. First, I’ve been digging a little bit more into the powerful data model definition abstraction of Core Data Services (CDS), following on from last week’s post in this Monday morning thoughts series, on abstraction.

Then, in browsing the content of my bookshelves, I came across a copy of a wonderful course on SAP assembler macro programming, called IT400 and written by Peter Skov.

(I’d lost my original copy; this new copy was courtesy of Chris Whealy, a good friend and fellow curious companion – he is one half of our shared blog Language Ramblings, which you may wish to peruse at some stage).

Finally, at the end of last week, I was honoured to be able to present a remote session at UI5con Bangalore, and I chose to speak about an introduction to functional programming techniques in JavaScript, given that is the native language of the UI5 toolkit.

 

Level of communication

These three things have something in common. Each relates to the way we communicate. Communicate to machines primarily, but actually to our fellow workers too. Although one might say that was the other way around – I read somewhere recently that only 10% of a programmer’s time is spent writing code. The other 90% is spent reading it. Trying to understand the logic, the intent and the general complexities of other people’s code. In some cases, of their own code, weeks or months later!

There’s a digression about code comments, how quickly they can go stale, and how unit testing is perhaps a better alternative for describing what code does, because the unit tests and the code cannot diverge in meaning or intent, like comments and code can do – this was a point made in the recent SAP Coffee Corner Radio podcast episode 4: “ABAP – The Special Snowflake with Nigel James and Graham Robinson“. But I’ll leave that for another time.

With CDS, the purity of abstraction is very evident with how data models are described. With the three-entity bookshop example in the Getting Started tutorial, even non-CDS initiates can understand the intent, even the detail, of what’s written. Moreover, how the definitions and relationships are implemented – in a database-specific way – are not important at this level. The beauty of this is that it allows the extension of definitions to other models in a high-level and natural way. (If you’re interested in learning more about this, see Oliver Welzel‘s post “ITelO – A Sample Business Application for the new Application Programming Model for SAP Cloud Platform“.)

In contrast, one cannot help but feel close to the machine when writing assembler. The first assembler I learnt was 6502, alongside Atom Basic – you could intertwine the two languages in a single program, for the Acorn Atom, which was one of the many features that made the Atom both quirky and much loved. With 6502 assembler or the mainframe 370 assembly language upon which the R/2 systems that I worked with was based (and the subject of the IT400 course), while you felt close to the machine, you were very far away from your fellow programmers and an abstract layer of intent.

You had to spell out exactly how you want the machine to go about some task, detailing the smallest and most mundane aspects – register storage and management, byte or word level processing, and so on. While of course a fellow programmer could read your code and understand it, it would take a while. Then again, with paper based forms being the equivalent of the activities we perform today with the Correction and Transport System (CTS) or any sort of Continuous Integration (CI) setup, everything moved slower anyway.

And so we come to the subject of Friday’s session at UI5con Bangalore. It’s a session I’ve given before in a number of different forms – at SAP TechEd in 2016 (DEV219 “Building More Stable Business Apps with Functional Techniques in JavaScript”), at UI5con in 2016 (“An Introduction to Functional Techniques in JavaScript for UI5”) and at UI5con@SAP in 2017 and 2018 in the form of a longer hands-on workshop “Functional programming for your UI5 apps”). It was for the hands-on format that I created a 20 page worksheet which is available for all online:

Functional Programming for your UI5 Apps – Hands-On Worksheet

 

The difference between how and what

In Part 1 of this worksheet I show the contrast between a low level, mechanical way to do something in JavaScript, and a higher level more abstract way. Moving from a “how you want the machine to do something” to a “what you want” is for me an underlying theme of functional programming, or at least a favourable by-product. Let’s dive into the example that I use.

We have a complex data structure of entities and samples for those entities (relating to the UI5 Explored app, in case you’re curious) and want to count the number of samples for the entities in a particular namespace. Here’s the traditional way of doing it:

var total = 0;
for (var i = 0; i < aEntities.length; i++) {
    var mEntity = aEntities[i];
    if (mEntity.namespace === "sap.ui.core") {
        total = total + mEntity.sampleCount;
    }
}

This for loop construction is not particular to JavaScript. The ideas expressed here, in the way we explain to the machine how to do something, can be found in very similar forms in many other languages. Beyond the fact that we’re mutating state (worse: mutating state outside the computation block, but let’s not go there), the key issue here is that even in this very simple form, there’s a lot going on, and it’s all rather mechanical.

We’re telling the machine *how* to go about checking each item in the aEntities array, giving it a lot of help with the use of the “i” variable to act as an incrementing array index. Doesn’t that strike you as rather low-level? There are similar constructs in the C language, and one could argue that C is as close to assembler as you can get without being assembler.

Surely there’s a way to do this without telling the machine how to process an array of items, without thinking at the level of language implementation?

Well, there is. Consider this equivalent:

aEntities
  .filter(x => x.namespace === "sap.ui.core")
  .reduce((a, x) => a + x.sampleCount, 0)

We can “say” this out loud as follows:

  • take the array of entities
  • filter them down to those whose namespace is “sap.ui.core”
  • fold* the filtered entities together, accumulating the “sampleCount” property values

*I’m deliberately using the word “fold” here because that is what reduce is called in other functional languages. But that too is a digression for another time.

At this level of abstraction, we’re thinking not in terms of mechanics, not in terms of instructing the machine how to go about working through a list of things one explicitly indexed item at a time. In the previous example we’re creating multiple variables that stay around after we’ve finished (and therefore things that we may have to now keep track of in our head when spending that 90% reading code). We’re doing that because we’re having to instruct the machine *how* to go about getting to the result.

In this second example, we have none of that. No variables (save for those in the transient and anonymous helper functions that we pass to filter and reduce respectively), and certainly no plodding instructions about how to process a list of items.

Instead, we are expressing *what* we want, in a higher level language. We don’t care how the array gets processed, we leave that to the language implementation*. List machinery is built in and comes for free with this more functional approach. In fact, if you’re interested in implicit list machinery like this, you might want to have a read of my post “The beauty of recursion and list machinery” over on Language Ramblings.

*if you’re wondering about performance, it’s not to say that the implementation of this intent is not done in a way that parallels the for-loop, internally.

I think there are powerful reasons for abstractions like those we find in functional programming and those we find in CDS, and one of those reasons relates to how we should perhaps constantly look to make things easier for ourselves, using the power of machinery to do the grunt work for us. Striving for the ability to express ourselves better, to talk in terms of what we want, rather than how to go about getting it, is surely a way we progress.

 

This post was brought to you by Pact Coffee’s Villa Rubiela and the peace and quiet of a very early (and damp) Monday morning in Manchester.

 

Read more posts in this series here: Monday morning thoughts.

 

Update 03 Aug: There’s now a recording of my remote session “Functional programming for you UI5 apps” available, a cut-down version of the worksheet in the form of a presentation. The recording is one in a whole series of recordings from the UI5con Bangalore event.

 

To report this post you need to login first.

8 Comments

You must be Logged on to comment or reply to a post.

  1. Joao Sousa

    As a “challenge” I postulate that the reason people keep using the for loop is that, developers actually “talk” that way. Looking at the reduce it probably won’t say much to most developers, while the for loop is the language they understand.

    I have experience in .Net where we used Lync and the communication problem was the same. Although I wrote more compact code, it was harder to read for most developer who were used to the foreach.

    People won’t shift if they don’t understand, and if they don’t understand they won’t shift.

    (1) 
    1. DJ Adams
      Post author

      Thanks Joao, there definitely is a challenge. I think your postulation is right, to an extent – developers have got used to talking that way, in the low-level imperative language of machines, because it’s where we came from (modulo LISP etc on the lambda calculus part of the language tree, of course!).

      I’m assuming for “Lync” you were rather referring to LINQ – if so, sounds good! I learned a lot from watching Erik Meijer’s talks (esp his series originally on Channel 9) and I understand he had a lot of influence and involvement in LINQ.

      I see your postulation, and raise you another postulation that if we don’t strive to move but stay within the bounds of our understanding, we’re making it very likely we’ll remain at the same level. Fighting talk, I know – but there you have it 😉

      (0) 
      1. Joao Sousa

        Lol, yes LINQ (I knew when I writing the something was off). I agree that we need to challenge ourselves, I try to do that at very day, but in this specific .Net project I was the only one “striving” and the other developers was having a hard time reading it.

        As I was the manager and had a budget to manage, I ended up “giving up” as the communication issues weren’t worth the small benefit.

        PS: There were little occurences of multiple Fors as far as I remember.

        (1) 
        1. DJ Adams
          Post author

          It does get a little depressing when progress (subjective or objective) is hindered by lack of budget, time or foresight. Not an unusual situation, and one that has held us back as an industry in general due to the way some projects are bid for and managed, in some cases (sweeping generalisation, I know, but hey). Especially painful if as the manager we are the ones having to make the tough decisions.

           

           

          (0) 
    2. Robin van het Hof

      But what if you have nested loops? In my experience, a lot of issues come from code which looks like this:

       

      // Get all flights with heavy luggage
      
      for (var i = 0; i < aFlights.length; i++) {
          const flight = aFlights[i];
          const aPassengers = flight.passengers;
      
          for (var j = 0; j < aPassengers.length; j++) {
              const passenger = aPassengers[j];
              const aLuggage = passenger.luggages;
      
              for (var k = 0; k < aLuggage.length; k++) {
                  const luggage = aLuggage[k];
      
                  if (luggage.isHeavy) {
                      // do something with `flight`
                  }
              }
          }
      }
      

      At the second for-loop, it is already pretty easy to mistakenly use [i] instead of [j], let alone at the third loop. You also need to make mental notes on what index variable you’re using for which array, making it harder for developers not familiar with your code to track what is going on.

       

      The functional way of coding the above is much easier to understand:

      aFlights
          .filter(flight => flight.passengers.filter(passenger => passenger.luggage.filter(luggage => luggage.isHeavy)))

       

      As an added benefit, you also get rid of those ugly if-statements.

       

      In a code review, I would definitely reject the first snippet, unless the developer has some solid explanation as to why it is the better approach.

      (2) 
      1. Joao Sousa

        Well you could argue that a better way to solve that problem would be to modularize the code, because the chain of filters is not very easy to read.

        But I do agree that chain “for”s are very hard to read, especially in the third one.

        (2) 
        1. Robin van het Hof

          Totally right about the modularisation. In the end, it’s all about readability (and thus maintainability) of your code.

           

          After all, you don’t write code for a computer to understand — you write code for developers to understand

           

          (1) 
      2. DJ Adams
        Post author

        Very true. And beyond JS of course, there are features in many functional languages that allow composition of functions, which would also help here in abstracting and modularising logic.

        (0) 

Leave a Reply