Skip to content

Instantly share code, notes, and snippets.

@dalegaspi
Last active June 8, 2021 17:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dalegaspi/1de4c34393f624551a8cbf21ea30763d to your computer and use it in GitHub Desktop.
Save dalegaspi/1de4c34393f624551a8cbf21ea30763d to your computer and use it in GitHub Desktop.
Arrow Functions and the Road to Declarative Programming

Arrow Functions and the Road to Declarative Programming

Consider this data:

let tbl_hs_students = {
  headers: [
    "First Name",
    "Year",
    "House",
    "Grade",
    "In Detention?"
  ],
  data: [{
      name: "Jamie",
      year: 4,
      house: "Lannister",
      grade: 3.0,
      detention: true
    },
    {
      name: "Cersei",
      year: 4,
      house: "Lannister",
      grade: 3.5,
      detention: true
    },
    {
      name: "Tyrion",
      year: 3,
      house: "Lannister",
      grade: 1.5,
      detention: false
    },
    {
      name: "Jon",
      year: 4,
      house: "Stark",
      grade: 2.0,
      detention: false
    },
    {
      name: "Bran",
      year: 1,
      house: "Stark",
      grade: 1.0,
      detention: false
    },
    {
      name: "Arya",
      year: 1,
      house: "Stark",
      grade: 3.0,
      detention: true
    },
    {
      name: "Daenerys",
      year: 4,
      house: "Targaryen",
      grade: 3.0,
      detention: true
    },
    {
      name: "Robert",
      year: 4,
      house: "Baratheon",
      grade: 1.0,
      detention: true
    },
    {
      name: "Theon",
      year: 2,
      house: "Greyjoy",
      grade: 2.5,
      detention: false
    }
  ]
};

If the question is:

"Give me the list of the names of the seniors who are not in detention"

Traditional way of thinking will probably lead you to this solution:

let lst = [];
for (i = 0; i < tbl_hs_students.data.length; i++) {
  if (tbl_hs_students.data[i].year == 4 && tbl_hs_students.data[i].detention) {
    lst.push(tbl_hs_students.data[i].name);
  }
}

Certainly, this is nothing wrong with the solution presented as it addressed the use case. However, it pertpetuates the thinking of "telling the program how to achieve the objective" instead of just "telling it what to do." The former is what is called "Imperative Programming" while the latter is called Declarative Programming."

Why is this such a big deal? This all falls under the umbrella of "Functional Programming" but I'm not going to dive into that as it's a broad and deep subject. There's a number of reasons why it has prevailed long before Object Oriented Programming is has even existed, let alone became popular. SQL falls under this. Think about it, when you want to get data from a table, you write something like this:

select name from hs_table_students where year = 4 and detention = 'true';

As you can see, you do not tell the query to create a list of names and any of that list operations, you just ask for the list of data that meets the criteria!

Simple and direct, isn't it? So is there a way to do this in JavaScript? Absolutely, and it looks like this:

let lst2 = tbl_hs_students.data.filter(s => s.year == 4 && s.detention).map(s => s.name);

The code is shorter, and requires less thinking. And that's the take-away here: it's easier to reason about, easier to understand and it is also less prone to bugs.

So what's this have to do with Arrow Functions? Declarative programming in JavaScript is nothing new; this has been around for quite some time with libraries like lodash and underscore (and more recent, rambda). The issue is that before ES5, it's a bit tedious to write and it makes it hard to read code that takes advantage of it. Before ES6, the above solution would have looked like this:

var lst3 = tbl_hs_students.data
  .filter(function(s) {
    return s.year == 4 && s.detention;
  })
  .map(function(s) {
    return s.name;
  });

As you can see: same effect, not as cool.

With that in mind, creating a Markdown table with the data above gets more interesting with the combination of the new ES6 template literals (string interpolation):

let markdown_table = [
  `| ${tbl_hs_students.headers.join(' | ')} |`,
  `| ${tbl_hs_students.headers.map(h => '-'.repeat(h.length)).join(' | ')} |`,
  tbl_hs_students.data
  .filter(s => s.year == 4 && s.detention)
  .map(d => `| ${Object.values(d).join(' | ')} |`)
  .join('\n')
].join(`\n`);

You can argue that the above example is a bit contrived, but this goes to show that if we're not careful, syntactic sugar and shortcuts makes it easy for us to abuse it and even make code less readable

let lst = [];
for (i = 0; i < tbl_hs_students.data.length; i++) {
if (tbl_hs_students.data[i].year == 4 && tbl_hs_students.data[i].detention) {
lst.push(tbl_hs_students.data[i].name);
}
}
let lst2 = tbl_hs_students.data.filter(s => s.year == 4 && s.detention).map(s => s.name);
var lst3 = tbl_hs_students.data
.filter(function(s) {
return s.year == 4 && s.detention;
})
.map(function(s) {
return s.name;
});
let tbl_hs_students = {
headers: [
"First Name",
"Year",
"House",
"Grade",
"In Detention?"
],
data: [{
name: "Jamie",
year: 4,
house: "Lannister",
grade: 3.0,
detention: true
},
{
name: "Cersei",
year: 4,
house: "Lannister",
grade: 3.5,
detention: true
},
{
name: "Tyrion",
year: 3,
house: "Lannister",
grade: 1.5,
detention: false
},
{
name: "Jon",
year: 4,
house: "Stark",
grade: 2.0,
detention: false
},
{
name: "Bran",
year: 1,
house: "Stark",
grade: 1.0,
detention: false
},
{
name: "Arya",
year: 1,
house: "Stark",
grade: 3.0,
detention: true
},
{
name: "Daenerys",
year: 4,
house: "Targaryen",
grade: 3.0,
detention: true
},
{
name: "Robert",
year: 4,
house: "Baratheon",
grade: 1.0,
detention: true
},
{
name: "Theon",
year: 2,
house: "Greyjoy",
grade: 2.5,
detention: false
}
]
};
let markdown_table = [
`| ${tbl_hs_students.headers.join(' | ')} |`,
`| ${tbl_hs_students.headers.map(h => '-'.repeat(h.length)).join(' | ')} |`,
tbl_hs_students.data
.filter(s => s.year == 4 && s.detention)
.map(d => `| ${Object.values(d).join(' | ')} |`)
.join('\n')
].join(`\n`);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment