I am playing with arrow functions in MongoDB
to write MapReduce jobs, and I noticed some weird behaviors.
I have a collection of students as follows:
{ "_id" : ObjectId("58ff3520704bd5539fe9dc7b"), "stud_id" : "x0000003", "name" : "david smith", "class" : "InfMgt", "weekNo" : 2, "attendencesThisWeek" : 4 }
{ "_id" : ObjectId("58ff3520704bd5539fe9dc7c"), "stud_id" : "x0000003", "name" : "david smith", "class" : "InfMgt", "weekNo" : 3, "attendencesThisWeek" : 2 }
{ "_id" : ObjectId("58ff3543704bd5539fe9dc7d"), "stud_id" : "x0000001", "name" : "sean smith", "class" : "edt", "weekNo" : 1, "attendencesThisWeek" : 2 }
{ "_id" : ObjectId("58ff3543704bd5539fe9dc7e"), "stud_id" : "x0000001", "name" : "sean smith", "class" : "edt", "weekNo" : 2, "attendencesThisWeek" : 2 }
...
Now my task is to write a MapReduce job that figures out the total attendences for each student to this date, so I started to write my functions with arrow functions:
The Map function
const mapFunc = () => emit(this.stud_id, this.attendencesThisWeek)
This is not emitting anything, after looking up online, I found that the arrow function already has a this
defined which makes it unsuitable as a map function here
inside an arrow function, this and arguments refer to the values of this and arguments in the environment the arrow function is defined in (i.e. "outside" the arrow function)
So I rewrote my function the "classic" way:
const mapFunc2 = function() {emit(this.stud_id, this.attendencesThisWeek)}
(I still use const
to make sure nothing is modifying the function or reassigning to mapFunc2
)
The Reduce function
When that was done, I though that there should be no problem with the reduce function since it is not using the this
keyword.
I was wrong!
const reduceFunc = (key, countAttendencesThisWeek) => Array.sum(countAttendencesThisWeek)
Yields quite funny looking results:
{ "_id" : "x0000002", "value" : (key, countAttendencesThisWeek) => Array.sum(countAttendencesThisWeek) }
{ "_id" : "x0000003", "value" : (key, countAttendencesThisWeek) => Array.sum(countAttendencesThisWeek) }
From going to the same stack overflow answer I read this:
ES2015 distinguishes between functions that are callable and functions that are constructable. If a function is constructable, it can be called with new, i.e. new User(). If a function is callable, it can be called without new (i.e. normal function call).
Functions created through function declarations / expressions are both constructable and callable. Arrow functions (and methods) are only callable. class constructors are only constructable.
My guess is that MongoDB is calling the reduce function in a way that makes it unwriteable as an arrow function, but here I need help to understand what is really happening.
I changed my reduce function to the "classic" version again:
const reduceFunc2 = function(keyStudId, attendences) { return Array.sum(attendences) }
Which is fine but leaves me wondering why here the arrow function is not usable.
Thank you for reading, this might be something of no importance for imperative programmers that learned that way, but I imagine if in years time some colleges start to teach functional first and arrow functions for example become the norm it will be quite weird for those developpers to have to adapt (if not understanding why things work that way).
see https://stackoverflow.com/a/34361380/4884034
EDIT:
- Version of db:
3.2.7
- Interpreter version:
MozJS-38
- Javascript engine:
mozjs