Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
qmacro
Developer Advocate
Developer Advocate
This is a searchable description of the content of a live stream recording, specifically "Ep.43 - SAP TechEd Functional Programming recap" in the "Hands-on SAP dev with qmacro" series. There are links directly to specific highlights in the video recording. For links to annotations of other episodes, please see the "Catch the replays" section of the series blog post.

This episode was streamed live on Tue 19 Nov 2019 and is approximately 90 minutes in length. The stream recording is available on YouTube.



Brief synopsis: At SAP TechEd 2019 in Bangalore we had this session "Write Solid Code with Functional Programming Techniques" where, using JS, we covered some functional programming aspects such as higher order functions, composition and reuse, partial application and immutability. It was a popular session and was repeated the next day. This episode gives us all the chance to look at the content of this session, and some related FP goodness.

00:04:30 An overview of what this episode is going to cover, including a recap of my Community Theatre session at SAP TechEd 2019 Bangalore "Write Solid Code with Functional Programming Techniques" which was repeated the next day due to popularity, which was nice. A quick shoutout to to my colleague mynyna.chau who ran the Community Theatre really well last week.

00:07:00 Reminded by A couple of my documents that I shared, covering more beginner functional programming topics:

00:07:30 Looking at the tweet that rsletta shared, describing a way to turn an array of records into a map (object) of those records, keyed by the value of a given property - this is also something that we are to cover in this episode.

00:07:50 A quick mention of Code Sandbox which seems a great online code editor for lots of web dev goodness.

00:12:00 Not to forget that Advent Of Code is almost upon us! We used a puzzle from Advent Of Code to warm our brains up at the very start of this series - if you're interested, check out Episode 0!

00:13:20 I think I've mentioned this video before, but it's worth mentioning again, which I did at this point - a great talk by Brian Lonsdorf (aka Dr Boolean) "Hey Underscore, You're Doing It Wrong!".

00:14:20 Finally, a quick pointer to a video on my own YouTube channel, "Implementing 'partition' three ways in JS and Ramda". Don't forget to please consider subscribing to my YouTube channel and let's see if I can get to 1000 subscribers by the end of the year!

00:15:30 OK, starting with the Northwind Products that we'll use as a small data set for our experiments.

00:17:50 Starting to work on producing a list of discontinued items for sale, with reduced prices, using a more "traditional" ("old fashioned"?) approach, with a for loop.

During this part, we see how many difficults we encounter, or rather cause for ourselves. Code and data that moves, changes. Worse, perhaps, is that we're having to instruct the computer how to work through a list, rather than simply what we want it to give us. But the ultimate issue is that we're clobbering data "upstream", our source of truth data set, without even realising! Not good, not now, and not further down the line when our program has grown and things are going wrong and we can't work out why.

Here's what the code looks like (this is the code that we don't like!):

aSaleItems = []

for (var i = 0; i < Products.length; i++) {
var product = Products[i]
if (product.Discontinued === true) {
product.UnitPrice = Math.round(product.UnitPrice * 0.9)
aSaleItems.push(product)
}
}


00:25:40 A small digression on the meaning of 'idempotent', which is an important concept in REST (and how idempotency is different from having side-effects).

00:27:50 It's clear that we don't want this, we want more "solid-state" programming. And for that we can adopt some functional programming techniques, which we start to have a look at now.

00:28:50 I explain the concepts that we covered in the Community Theatre talk, and chris.whealy kindly writes them down in the chat for me (thanks Chris!) ready to cover in this episode now:

  • higher order functions

  • reuse and chaining

  • partial application

  • immutability


00:31:10 Talking about the "triumvirate" of functions map, filter and reduce and pointing out that reduce is the mother of all functions, in that, for example, you can implement both map and filter using reduce.

A bit later on, we note that these functions have built-in "list machinery" - see my post "The beauty of recursion and list machinery" if you want to dig into this a little more.

00:32:40 Starting to build out the functional style equivalent, beginning with a simple isDiscontinued function definition.

isDiscontinued = x => x.Discontinued === true


00:45:50 A bit later on we also define a helper function for discounts, which is written in such a way that we can partially apply it:

applyDiscount = percent => price => Math.round(price * ((100 - percent) / 100))


Then we can create new functions based on a partial application of applyDiscount thus:

tenPercentOff = applyDiscount(10)


which will allow us to call it like this:

tenPercentOff(200)
//> 180


00:49:50 At this stage we have our new, solid-state equivalent code, which looks like this:

Products
.filter(isDiscontinued)
.map(x => {
let { ProductName: Name, UnitPrice } = x
return {
Name,
SalePrice: tenPercentOff(UnitPrice)
}
})


Doesn't that make you feel a lot more comfortable? No mutating of data, no moving parts (just implicit list machinery); solid-state code that just evaluates. Lovely!

By the way - we also used some other ES6 facilities in this code, can you remember and name them? Put your thoughts in the comments below.

00:54:20 Mentioning the concept of a Functor, which is a lot less scary than the Wikipedia article might lead you to believe, and is a nice word to put on a tshirt 🙂

00:55:00 Starting to look at the next section, specifically the "challenge" set in the tweet from Kyle Shevlin.

00:56:40 There is, in fact, a higher order function find, a sibling, so to speak, of map, filter and reduce, and it's quite effective and easy to read, but can of course have performance issues if the data set is large. Here's what we did with find, again on the Products data set, looking for the product record for 'Ikura':

Products.find(x => x.ProductName === 'Ikura')
//> {ProductID: 10, ProductName: "Ikura", SupplierID: 4, ... }


00:59:00 Effectively, we want to reshape (a term I learned from APL) the Products data like this:
[ ... ] -> { ... }

In other words, from an array to an object. Given that map takes an array and produces an array (as does filter), we can't use it. But we can use reduce, which we can use to produce a result of any shape!

01:01:00 The reduce function is often used to reshape an array of data to a scalar, for example tranforming a list of numbers ...

nums = [1,2,3]


... to what they all add up to:

nums.reduce((a, x) => a + x, 0)
//> 6


This is the equivalent of this "long hand" version:

nums.reduce(function(accumulator, item) {
accumulator = accumulator + item
return accumulator
})


01:03:00 Looking briefly at the documentation for reduce, on MDN, the Mozilla Developer Network and a great resource for JS docu.

01:12:20 Now we're ready to produce an object from the Products array, keyed on the product name. We do it manually, directly, first of all, and this is what it looks like:

Products
.reduce((a, x) => { a[x.ProductName] = x; return a }, {})
//> { Chai: {...}, Chang: {...}, ... }


The key thing to note here is that the starting value is an empty object, which puts us on the right path for our desired transformation:
[ ... ] -> { ... }

01:15:20 Now we think about adding this functionality to the Array object itself, to make it available on all arrays. To discover how to add a function to the Array prototype, we explore first, with a silly example:

Array.prototype.someRandomThing = function(n) { return this.length * n }
[1,2,3,4].someRandomThing(5)
//> 20


Note that we use the function() { ... } approach because we need to refer to this (to get to the actual array instance) which is not available in fat-arrow based function definitions.

01:18:00 Now we're ready. This is what we end up with:

Array.prototype.makeObjectOn = function(prop) {
return this.reduce((a, x, i) => { a[x[prop] || i] = x; return a }, {})
}


Note that we're using the third parameter i that's passed to the callback function, to use a fallback index parameter if the one supplied doesn't exist.

01:28:00 As a final flourish, we add a really nice affectation which allows us to do away with multiple statements inside the inner function - this was introduced to me by chris.whealy a while ago, and allows us to write functions with single expressions only:

Array.prototype.makeObjectOn = function(prop) {
return this.reduce((a, x, i) => (_ => a)(a[x[prop] || i] = x, {})
}


Take a few minutes to stare at this for a bit, especially the construction (_ => a)(...), which is an immediately invoked function expression (IIFE). You may grow to love it 🙂
1 Comment