Susan Harkins (207) [Avatar] Offline
#1
Please post errors found in the published version of Functional Programming in JavaScript here. We'll publish a comprehensive list for everyone's convenience. Thank you!

Susan Harkins
Errata Editor
Manning Publications
409046 (1) [Avatar] Offline
#2
Chapter 5 code has big problems! The most critical is the Maybe lacks a "chain" method. I know we can make the container that exhibits the behavior we need so not implementing "ap" is convenient for pedagogical reasons but we do need to promote some kind of standard and as far as I know fantasyland is the only shot at that.

There is a lot of this.value that should be this._value.

and 'static' left should be 'static Left'
345238 (2) [Avatar] Offline
#3
on page 12 in Chapter 1,

var csv = (student) {
return `${student.ssn}, ${student.firstname}, ${student.lastname}`;
};

should there be "function" before "(student)"?
345238 (2) [Avatar] Offline
#4
Chapter 1, page 14

var plus2 = run(increment, increment);
print(run(0)); //-> 2

should it be print(plus2(0)); //-> 2 ?
414871 (12) [Avatar] Offline
#5
pg 25. "oriented-oriented" -> "object-oriented"
414871 (12) [Avatar] Offline
#6
pg.40

people.sort( ( p1, p2 ) => p1.getAge() > p2.getAge() );


(missing comparison operator)
414871 (12) [Avatar] Offline
#7
pg 43

"...and so son" -> "...and so on"
414871 (12) [Avatar] Offline
#8
pg 77, 2nd paragraph

"...how think recursively." -> "...how to think recursively."
414871 (12) [Avatar] Offline
#9
pg 94

const name = curry2( (last, first) => new StringPair( last, first ) );
414871 (12) [Avatar] Offline
#10
pg 122, Listing 5.1

Inconsistent references to `this._value`. e.g., `this.val`, `this.value`.
414871 (12) [Avatar] Offline
#11
pg. 123, ¶1

"...it can't directly retrieved..." -> "...it can't be directly retrieved..."
klmgerber (2) [Avatar] Offline
#12
p. 64
_(persons).reverse().map(...)

This follows the native array behavior, so it still mutates the array without making a copy. You can just use:
persons.slice().reverse().map(...)

John-David Dalton who maintains Lodash has a discussion on this topic here:
https://github.com/lodash/lodash/issues/224
Joseph Hu (1) [Avatar] Offline
#13
414871 wrote:pg.40

people.sort( ( p1, p2 ) => p1.getAge() > p2.getAge() );


(missing comparison operator)


Comparator should return an integer (positive, negative, or zero).

So I think that should be
people.sort( ( p1, p2 ) => p1.getAge() - p2.getAge() );


414871 (12) [Avatar] Offline
#14
pg 139

..
return Either.Left(`Object not found...`);


to

return Either.left(`Object not found...`);


414871 (12) [Avatar] Offline
#15
p. 179

'...reduces the complexity if your program as a whole.'

to

'...reduces the complexity of your program as a whole.'
414871 (12) [Avatar] Offline
#16
p. 182

'...requires 8 byes each.'

to

'...requires 8 bytes each.'
proebuck (37) [Avatar] Offline
#17
pg. 7

...now easily augment printMessage to repeat the message twice...

var printMessage = run(console.log, repeat(3), h2, echo);



Shouldn't that be
repeat(2)
?
proebuck (37) [Avatar] Offline
#18
pg. 27, fig 2.2

p.fullname;   //-> Alonzo Church


Should this have been

person.fullname();   //-> Alonzo Church


?
418941 (7) [Avatar] Offline
#19
In section 4.3: Curried function evaluation
Here’s the formal definition of a curry of three parameters:
curry(f) :: (a,b,c) -> f(a) -> f(b)-> f(c)


The above definition seems to be wrong. Why function f is used to call on parameters a, b and c like f(a), f(b) and f(c)? To me, this is misleading.

A more proper definition of currying a function f that takes two parameters (a, b,c) and return a value d should have been:
curry(f) :: ((a,b,c) -> d) -> a -> b -> c -> d
418941 (7) [Avatar] Offline
#20
In section 4.5.2
The composition of g :: A -> B and f :: B -> C, pronounced (“f composed of g”), results in another function (arrow) from A -> C,
as shown in figure 4.11. This can be expressed more formally as
f • g = f(g) = compose :: (B -> C) -> (A -> B) -> (A -> C)


Based on the implementation of compose in listing 4.8 where the compose function takes n parameters/functions and return a new function, the above should have been expressed as below as per my understanding. Here, compose takes two functions as parameter and return a new function.
f • g = f(g) = compose :: ((B -> C), (A -> B)) -> (A -> C)


Both the curried and un-curried definition are correct. However, in the context the author is explaining, the second definition seems to make more sense.
justin-c (23) [Avatar] Offline
#21
In section 5.3.2 Recovering from failure with Either, page 140 at the bottom there's:

"This function was created in section 4.4.2" (with reference to parseUrl).

It should be "This function was created in section 4.4.1 (Extending the core language)"
justin-c (23) [Avatar] Offline
#22
Listing 5.11, logging in code and stated log output do not match.
Logan Chien (1) [Avatar] Offline
#23
Page 65, Listing 3.2:

function reduce(arr, fn, [accumulator]) {


should be:

function reduce(arr, fn, accumulator) {


The brackets surrounding
accumulator
should be removed. Otherwise, it will be treated as destructing binding in ES2015 and a syntax error in earlier Javascript version.
418941 (7) [Avatar] Offline
#24
4.6.2. Tap (K-combinator)
Here’s the function signature:

tap :: (a -> *) -> a -> a


Based on reply from question I asked in stack overflow...
http://stackoverflow.com/questions/38614679/function-signature-of-tap-k-combinator

1. What is the meaning of star (*) ? The author should make it clear. It means anything. It would have been more correct if * is replaced by b:

tap :: (a -> b) -> a -> a


2. Do you mean Tap function is K-combinator? K-combinator can't have side effects in pure functional terms. K-combinator has some surprising applications like the tap function. Tap is not a K-combinator and the author should clarify this point.
418941 (7) [Avatar] Offline
#25
418941 wrote:In section 4.3: Curried function evaluation
Here’s the formal definition of a curry of three parameters:
curry(f) :: (a,b,c) -> f(a) -> f(b)-> f(c)


The above definition seems to be wrong. Why function f is used to call on parameters a, b and c like f(a), f(b) and f(c)? To me, this is misleading.

A more proper definition of currying a function f that takes three parameters (a, b, c) and return a value d should have been:
curry(f) :: ((a,b,c) -> d) -> a -> b -> c -> d
418941 (7) [Avatar] Offline
#26
there is already a duplicate entry...so removing it.
Please ignore this.
211247 (1) [Avatar] Offline
#27
p17 missing 'of'
p.17 First full paragraph, 2nd line reads: 'In reality, run is an alias for one [of] the most important techniques: composition."
The 'of' is missing, I've indicated where with the brackets.
cdoremus (8) [Avatar] Offline
#28
There are a number of errors in Listing 3.1 on page 64. Here is a correct version:
function map(arr, fn) {
	let idx = 0,
	index = 0,
	len	= arr.length,
	result = new Array(len);
	while (++idx < len) {
		result[index++] = fn(arr[idx-1], idx, arr);
	}
	return result;
}

Susan Harkins (207) [Avatar] Offline
#29
331872 (109) [Avatar] Offline
#30
Million thanks to everyone for catching these smilie
4943 (2) [Avatar] Offline
#31
Chapter 2, page 51 (printed edition)

The function MyModule needs a return statement to work


var MyModule = (function MyModule(export)
    ...
    return export;
}(MyModule || {}));
331872 (109) [Avatar] Offline
#32
Good catch. Sorry I missed that. Updating the errata with this.
4943 (2) [Avatar] Offline
#33
Missing get method

On p131:

studentAddress('444-44-4444').join().get():


There is no get() method in Wrapper, so this has to be added to it (p130)

get() {
    return this._value;
}
331872 (109) [Avatar] Offline
#34
Fixed. Thanks!
418941 (7) [Avatar] Offline
#35
Susan Harkins (207) [Avatar] Offline
#36
Thank you for reporting this. The link above works and I've repeated it below for your convenience:
https://manning-content.s3.amazonaws.com/download/5/3fb93a3-8e97-4328-9abc-8f23b5b9893c/Atencio_FuntionalProgrammingInJavaScript_Err4.html
SteveS (6) [Avatar] Offline
#37
cdoremus wrote:There are a number of errors in Listing 3.1 on page 64. Here is a correct version:
function map(arr, fn) {
	let idx = 0,
	index = 0,
	len	= arr.length,
	result = new Array(len);
	while (++idx < len) {
		result[index++] = fn(arr[idx-1], idx, arr);
	}
	return result;
}


Unfortunately, this still does not work correctly -- the last element in the array is not processed. The condition in the while loop needs to use postfix increment rather than prefix increment.

However, I find a for loop to be more compact and understandable for this problem:

function map(arr, fn) {
  const len = arr.length,
        result = new Array(len);
  for (let idx = 0; idx < len; ++idx) {
    result[idx] = fn(arr[idx], idx, arr);
  }
  return result;
}


See this JS Bin snippet for sample runs.
331872 (109) [Avatar] Offline
#38
I ran the JSBin. Looks great. Thanks for correcting the errata. We'll get this updated ASAP.

381438 (3) [Avatar] Offline
#39
Am I crazy here?
I spent entirely too long thinking about this, but it may be errata instead of my misunderstanding.
In multiple instances we are using the Person class and setting it.
Let's take chapter three for example:

const p1 = new Person('Haskell', 'Curry', '111-11-1111');
p1.address = new Address('US');
p1.birthYear = 1900;


I tried running this in Babel and received:
Cannot set property address of #<Person> which has only a getter

Please see the code here BABEL LINK

I explain further in the Babel link above. We are supposed to be using '_address' we are now updating the property of the object, not 'address' which is the argument passed into the original function.

If I am right, I highly advise changing this immediately as it is confusing and similar examples are used in multiple instances. Thanks
331872 (109) [Avatar] Offline
#40
Hey,

I checked your link and you're missing the setter on the Person class:

set address(addr) {
     this._address = addr;
}


If you look at page 28, where I define Person, the setter method is there. This would explain the error you're seeing.

Let me know if this helps.
SteveS (6) [Avatar] Offline
#41
On p. 161, a test case is provided for the fork combinator:

QUnit.test('Functional Combinator: fork', function (assert) {
    const timesTwo = fork((x) => x + x, R.identity, R.identity);
    assert.equal(timesTwo(1), 2);
    assert.equal(timesTwo(2), 4);
});


As written, the timesTwo function ignores the behavior of func2. It happens to pass only because R.identity is specified for both func1 and func2, which ultimately aliases the behavior of these parameters with respect to the given join function. That is, this test would continue to pass if fork was incorrectly implemented as

const fork = function(join, func1, func2){
    return function(val) {
        return join(func1(val), func1(val));
    };
};


I believe the timesTwo function should actually be defined as

const timesTwo = fork((x, y) => x + y, R.identity, R.identity);


to be technically correct. Otherwise, it is not actually testing that func2 receives val and that the return value of func2 is passed to join. These behaviors (among others) are really what we are attempting to verify when testing a combinator.

I agree with the general point made in the surrounding text about using simple functions to test combinators, but for this particular test case, using the same function for both func1 and func2 may not have been the best choice because of the potential for aliasing. It's probably not appropriate at this time, but maybe in the second edition smilie, this example could be improved to use unique arguments for func1 and func2 as well as elaborate on why that would be important from a testing perspective, as discussed above.
331872 (109) [Avatar] Offline
#42
Makes sense. Looking back at it, yes you're right. There's room for improvement there.

Thanks!
Susan Harkins (207) [Avatar] Offline
#43
The most recent errata list is available at https://manning-content.s3.amazonaws.com/download/e/95d6cc5-a4a1-4e8e-a75c-02aaede8cafa/Atencio_FuntionalProgrammingInJavaScript_Err9.html. Thanks!

Susan Harkins
Errata Editor
Manning Publications
jmilkiewicz (12) [Avatar] Offline
#44
On page 149 an introduction of liftIO (or at least the way it is defined) seems to be wrong...

const liftIO = function (val) {
   return IO.of(val);
};


const append = curry(function (elementId, info) {
   document.querySelector(elementId).innerHTML = info;
   return info;
});

const showStudent = R.compose(
   map(append('#student-info')),
   liftIO,
   map(csv),
   map(R.props(['ssn', 'firstname', 'lastname'])),
   chain(findStudent),
   chain(checkLengthSsn),
   lift(cleanInput));


In that case IO monad will be created with Either ( IO.of(<Either>smilie).
It would mean that this Either would be fed to append function - which is wrong according to its definition !

So one of solutions would be to simply have "a new" append function that takes Either parameter instead of info
The other option (which i prefer more) would be to introduce new function that will "prepare and massage" data for IoMonad

const getOrElse = R.curry((els, container) => container.getOrElse(els));


and then wire it to a chain:
const showStudent = R.compose(
   map(append('#student-info')),
   liftIO,
   getOrElse('unable to find a student'),
   map(csv),
   map(R.props(['ssn', 'firstname', 'lastname'])),
   chain(findStudent),
   chain(checkLengthSsn),
   lift(cleanInput));


BTW i do not like this sample in a book as using Either with null-value Left makes no sense. It is much better to use Maybe in that case. I would prefer to have:


const safeFindObject = R.curry((db, id) => {
  const val = find(db, id)
  return val? Either.right(val) : Either.left("can not find a student");
});

const checkLengthSsn = ssn => {
  return validLength(9,ssn)) ? Either.right(ssn): Either.left('invalid SSN'); 
};


and then make the flow:



//it assumes that both Left and Right can have value. The only difference between Left and Right would be that chain/map of Left return //this and Right would do proper mapping/chaining applying function parameter
const getValue = (either)=>either.value;

const showStudent = R.compose(
   map(append('#student-info')),
   liftIO,
   getValue,
   map(csv),
   map(R.props(['ssn', 'firstname', 'lastname'])),
   chain(findStudent),
   chain(checkLengthSsn),
   lift(cleanInput));



331872 (109) [Avatar] Offline
#45
Thanks for the feedback. I agree with you that simplifying those helper methods is better. I decided to keep the running code as you suggested:

const getOrElse = R.curry((message, container) => container.getOrElse(message));

const showStudent = R.compose(    
  map(append('#student-info')), 
  liftIO,
  getOrElse('unable to find student'),
  map(csv),
  map(R.props(['ssn', 'firstname', 'lastname'])),
  chain(findStudent),
  chain(checkLengthSsn),
  lift(cleanInput)
);


The reason I prefer this is that extacting a value from Left will throw an exception as per the monadic contract. Instead, getOrElse() does the trick and it's very elegant with Either because in the event the student object is not found, a general error flows through indicating "Object with ID xxx not found". It's only when the program reads it and then makes it more contextual to say "unable to find student". I think having that level of traceability is nice.

We will get this fixed ASAP.

Thanks!
jmilkiewicz (12) [Avatar] Offline
#46
on p 194 of listing 7.2
const testRot13 =
     IO.of(start)
       .map(R.tap(start('rot13')))
       .map(R.tap(test(
            rot13,
             'functional_js_50_off'
       )))
       .map(end);


There are 2 errors:
1. It shall be IO.from instead of IO.of as you wanna get the timer lazily.
2. .map(R.tap(start('rot13'))) is wrong - you got TypeError: fn is not a function and i would recommend to remove it completely

IMHO the folllowing fragment shall be:

const testRot13 = IO.from(start)
      .map(R.tap(testFunction(rot13, 'functional_js_50_off')))
      .map(end);

331872 (109) [Avatar] Offline
#47
Thanks. Yes. Verified it. I had changed the IO monad mid-stream and missed this unit test. So, I added it back in.

Thanks for helping me catch this, we're in the process of updating the book with the errors fixed. So, we would love to get you a new copy once it comes out.

Really appreciate your help.
jmilkiewicz (12) [Avatar] Offline
#48
Listing 8.4

Instead of
_selector('#search-button').addEventListener('click', handleMouseMovement);


It shall be

_selector('#search-button').addEventListener('click', handleClickEvent);
jmilkiewicz (12) [Avatar] Offline
#49
On p 212. There is a code snippet
for (let i = 0; i < students.length; i++) {
   let student = students[i];
   if (student.address.country === 'US') {
      getJSON(`/students/${student.ssn}/grades`,
         function (grades) {
            showStudents(student, average(grades));
         },
         function (error) {
           console.log(error.message);
} );
} }


Author is claiming that the code is wrong
Regardless of using the block-scoped keyword let, all the inner calls to showStudents(student, average(grades)) see the last student object reference in its closure, displaying the same student record.


I think it is wrong, let will create a new variable here so everything shall be ok. I do not feel like implementing the whole logic in browser but i implemented "equivalent" code in node.js which shows that the code shall be fine...

  const students = [
    {
      name: 'Aaa',
      ssn: 1,
      country: 'US'
    },
    {
      name: 'BBB',
      ssn: 2,
      country: 'PL'
    },
    {
      name: 'CCC',
      ssn: 3,
      country: 'US'
    }
  ];

  const grades = {
    1: [90, 90],
    2: [80, 80],
    3: [80, 90]
  };

  const myFunc = () => {
    for (let i = 0; i < students.length; i++) {
      let student = students[i];
      if (student.country === 'US') {
        getJSON(`${student.ssn}`,
          function (grades) {
            showStudents(student, average(grades));
          },
          function (error) {
            console.log(error.message);
          });
      }
    }
  };

  const getJSON = (ssn, succ, error) => {
    setTimeout(succ, 100, grades[ssn]);
  };

  const average = (studentGrades) => R.sum(studentGrades) / R.length(studentGrades);

  const showStudents = (student, avg) => {
    console.log(`${student.name} got ${avg}`);
  };


Of course still a code presented in a book (definition of myFunc) is bad and shall be avoided, but IMHO the code is "valid".
If one changes declaration of student to var instead of let. Then we will face the problem as described in a book.
jmilkiewicz (12) [Avatar] Offline
#50
on p 213

const showStudentsGrades = R.curry(function (student, grades) {
   appendData(student, average(grades));
});

is simply not needed

For CPS to work it would be simply enough to:


for (let i = 0; i < students.length; i++) {
   processStudent(students[i]);
}

const processStudent = function (student) {
   if (student.address.country === 'US') {
     getJSON(`/students/${student.ssn}/grades`,
         function (grades) {
            showStudents(student, average(grades));
         },
         function (error) {
           console.log(error.message);
} );
} ;


Now only a 'single' student parameter is accessible for showStudents function and it will not be changed by loop iteration
331872 (109) [Avatar] Offline
#51
Thanks. Yes, sorry for those typos. I've corrected that. Some of those were issues with some editing on my part that happened afterward. For instance, the second block of code should have been using "var" instead of "let". We'll change those in the re=-print. Thank you for spotting it and for all the good work in helping me find these smilie
414871 (12) [Avatar] Offline
#52
pg. 128

Empty is missing the fmap function (https://github.com/luijar/functional-programming-js/blob/master/src/model/Empty.js). This is used in the subsequent code example on pg. 129.
414871 (12) [Avatar] Offline
#53
Some of the ES6 fat arrow syntax errata updates on the Errata page could be further improved by removing the extraneous returns and braces, for example:


Page 94. Second code sample

Rework code to ES6 syntax

const name = curry2((last, first) => {
return new StringPair(last, first);
});


This could be just:

const name = curry2((last,first) => new StringPair(last,first));


There are a few other similar instances of this on the Errata page.
428568 (3) [Avatar] Offline
#54
On page 37, the code

var lastnameLens = R.lenseProp('lastName');


should be

var lastnameLens = R.lensProp('lastname');

331872 (109) [Avatar] Offline
#55
The lens code had been corrected, it's awaiting publishing. But thanks for catching it.

As far as the return statements, I had cleaned those up, but I have to be mindful to not displace the pages when we print the new version with all typos fixed, that's why I had left them like that in the meantime. I agree this is important for readability of more idiomatic FP, so I'll talk with the editors to see how we can get all of those in.

Thank you! smilie
Winson (28) [Avatar] Offline
#56
p67

the code should be

const countryPath = ['address','country']; <- original text 'city' is undefined field
const countryLens = R.lens(R.path(countryPath), R.assocPath(countryPath));

then

_(persons).map(R.view(countryLens)).reduce(gatherStats,{});
331872 (109) [Avatar] Offline
#57
I'll make a note. All of this is in the process of being cleaned up and this helps a lot. If you spot anything else, please let me know.

Thanks!
144402 (1) [Avatar] Offline
#58
On page 202 the two versions of recursive sum use lodash

_.rest(arr)


I get an error that rest expects a function. I think the author meant to use
_.tail(arr)


Also the tail-recursive version doesn't return the correct value.
if (_.isEmpty(arr)) {
  return 0;
}


This should return the accumulator, as otherwise every call will return 0.
if(_.isEmpty(arr)) {
  return acc;
}