<commit_before># Purpose of this fork

This is my fork of the original 
[fpinscala/fpinscala](https://github.com/fpinscala/fpinscala)
repository for the book ["Functional Programming in Scala"](http://www.manning.com/bjarnason/). 
The purpose is to do the exercises and compare to the model answers.

# Learnings

## Notes

| Exercises   | Notes  |
| ---         | ---    |
| 2.1         | I thought of an elegant solution for an efficient tail recursive Fibonacci function. |
| 3.1 - 3.6   | I had a redundant case statement in exercise 3.5 and 3.6. |
| 3.6         | Q: Why can't the init function be implemented in constant time like tail? A: There are a long chain of pointers referencing the last item, so removing that last item causes cascading changes all the way up the structure. |
| 3.7         | Q: Can product, implemented using foldRight, immediately halt the recursion if it encounters a zero? A: Not without remaining type-agnostic. Possibly define a foldRightWhile function taking an extra predicate and outcome if false. |
| 3.8         | Q: foldRight(List(1,2,3), Nil: List[Int])(Cons(_,_))) = Cons(1,Cons(2,Cons(3,Nil))) = List(1,2,3). What does this say about their relationship? A: foldRight with Cons is the identity function for lists? }
| 3.9 - 3.12  | No comments |
| 3.13        | How to write foldLeft in terms of foldRight and vice versa... reverse(foldLeft(List(1,2,3), Nil:List[Int])((l, a) => Cons(a, l))) == foldRight(List(1,2,3), Nil:List[Int])((a, l) => Cons(a, l)) == Cons(1,Cons(2,Cons(3,Nil))) |
| 3.14        | No comments |
| 3.15        | Implemented concatListOfLists as a foldRight over append. append is linear in the first list, but foldLeft keeps on re-generating the first term, so the effect is quadratic. So I changed to foldRight to fix this. |
| 3.16        | Why don't underscores work? def addOne(l: List[Int]): List[Int] = foldRight(l, Nil: List[Int])((a,b) => Cons(a + 1, b)) but not def addOne(l: List[Int]): List[Int] = foldRight(l, Nil: List[Int])(Cons(_ + 1, _)) |
| 3.17        | No comments |
| 3.18        | Generalizing map is easy via foldRight. But the model answer also give two other implementations that are safer against stack overflows. |
| 3.19 - 3.21 | filter, flatMap and filter via flatMap. Note that I returned "Nil: List[A]" where Nil would be sufficient, according to model answer. |
| 3.22 - 3.23 | No comments |
| 3.24        | My implementation appears fine. But the model answer is slightly better, because it is more obvious that all possible cases have been handled. |
| 3.25 - 3.27 | No comments |
| 3.28        | I don't think I agree with the discussion of ADTs and encapsulation on page 46 of the book. Encapsulation is about allowing the implementation to be changed without code changes cascading to consumers of the interface. Mutability is moot. |
| 3.29        | 3 parameter lists were used, not 2, so that the g: (B,B) => B could benefit from type inference over the previous f: A=>B. In some cases the types needed to be specified after the function name in the function call, to specify the most base type. |
| 4.1         | Fine, with a minor tweak: I passed None: Option[B] in the implementation of flatMap. There was no need to specify the type of None. |
| 4.2         | A bit tricky - remember that map and flatMap can operate on Options as easily as sequences. See tip 5 below for noticing opportunities to use map or flatMap instead. |
| 4.3         | Pretty easy, based on using tip 5 for working out when to use map or flatMap |
| 4.4         | Quite easy, using map2. The model answer uses a recursive call as its first solution, which is a bit different. |
| 4.5         | No comments |
| 4.6         | A bit tricky |
| 4.7         | Either.traverse() and sequence(). A bit tricky. I didn't see that map2 could be used to implement traverse. |


## Tips

1. Check pattern matches for redundant case statements i.e. where a more general case statement covers an earlier specific case.
2. When using foldRight, consider using foldLeft on the reversed list or a mutable local variable for greater efficiency and safety against stack overflows.
   _Edit: Not necessary... The [Scala 2.11.0 implementation](https://github.com/scala/scala/blob/v2.11.0/src/library/scala/collection/immutable/List.scala#L399) does this already_
3. Nil: List[B] is necessary in fold calls to bind the return type. Just Nil is probably sufficient in the implementation as the type is already bound.
4. In pattern matches, see if there is a way to make the last clause a "case _ => ...", as this ensures that all scenarios have been covered.
5. See opportunities for using map or flatMap on an option by looking for pattern match expression where None maps to None, and Some(?) maps to an expression. If the result is a Some(expr) convert to map(expr). If the result can evaluate to Some or None, then use flatMap.
6. In exercise 4.6, Left[E] and Right[A] only implement one type parameter, so one must typecast to Either[E,A] to get correct signature for map, flatMap, etc.
7. In 4.7, "(_)" didn't work for an identity map. I thought it would be necessary to specify the type of the parameter. But "(x => x)" would have worked.


## Thoughts

1. It sucks that Scala doesn't have automatic currying and type inference over all parameters. Multiple parameter lists is a clever but kludgy idea.
2. I don't have a mental model for when (_) will work as an identity map, and when (x => x) is required (see exercise 4.7). TODO: Update when I understand.

<commit_msg>Note on poor names in Either data type<commit_after># Purpose of this fork

This is my fork of the original 
[fpinscala/fpinscala](https://github.com/fpinscala/fpinscala)
repository for the book ["Functional Programming in Scala"](http://www.manning.com/bjarnason/). 
The purpose is to do the exercises and compare to the model answers.

# Learnings

## Notes

| Exercises   | Notes  |
| ---         | ---    |
| 2.1         | I thought of an elegant solution for an efficient tail recursive Fibonacci function. |
| 3.1 - 3.6   | I had a redundant case statement in exercise 3.5 and 3.6. |
| 3.6         | Q: Why can't the init function be implemented in constant time like tail? A: There are a long chain of pointers referencing the last item, so removing that last item causes cascading changes all the way up the structure. |
| 3.7         | Q: Can product, implemented using foldRight, immediately halt the recursion if it encounters a zero? A: Not without remaining type-agnostic. Possibly define a foldRightWhile function taking an extra predicate and outcome if false. |
| 3.8         | Q: foldRight(List(1,2,3), Nil: List[Int])(Cons(_,_))) = Cons(1,Cons(2,Cons(3,Nil))) = List(1,2,3). What does this say about their relationship? A: foldRight with Cons is the identity function for lists? }
| 3.9 - 3.12  | No comments |
| 3.13        | How to write foldLeft in terms of foldRight and vice versa... reverse(foldLeft(List(1,2,3), Nil:List[Int])((l, a) => Cons(a, l))) == foldRight(List(1,2,3), Nil:List[Int])((a, l) => Cons(a, l)) == Cons(1,Cons(2,Cons(3,Nil))) |
| 3.14        | No comments |
| 3.15        | Implemented concatListOfLists as a foldRight over append. append is linear in the first list, but foldLeft keeps on re-generating the first term, so the effect is quadratic. So I changed to foldRight to fix this. |
| 3.16        | Why don't underscores work? def addOne(l: List[Int]): List[Int] = foldRight(l, Nil: List[Int])((a,b) => Cons(a + 1, b)) but not def addOne(l: List[Int]): List[Int] = foldRight(l, Nil: List[Int])(Cons(_ + 1, _)) |
| 3.17        | No comments |
| 3.18        | Generalizing map is easy via foldRight. But the model answer also give two other implementations that are safer against stack overflows. |
| 3.19 - 3.21 | filter, flatMap and filter via flatMap. Note that I returned "Nil: List[A]" where Nil would be sufficient, according to model answer. |
| 3.22 - 3.23 | No comments |
| 3.24        | My implementation appears fine. But the model answer is slightly better, because it is more obvious that all possible cases have been handled. |
| 3.25 - 3.27 | No comments |
| 3.28        | I don't think I agree with the discussion of ADTs and encapsulation on page 46 of the book. Encapsulation is about allowing the implementation to be changed without code changes cascading to consumers of the interface. Mutability is moot. |
| 3.29        | 3 parameter lists were used, not 2, so that the g: (B,B) => B could benefit from type inference over the previous f: A=>B. In some cases the types needed to be specified after the function name in the function call, to specify the most base type. |
| 4.1         | Fine, with a minor tweak: I passed None: Option[B] in the implementation of flatMap. There was no need to specify the type of None. |
| 4.2         | A bit tricky - remember that map and flatMap can operate on Options as easily as sequences. See tip 5 below for noticing opportunities to use map or flatMap instead. |
| 4.3         | Pretty easy, based on using tip 5 for working out when to use map or flatMap |
| 4.4         | Quite easy, using map2. The model answer uses a recursive call as its first solution, which is a bit different. |
| 4.5         | No comments |
| 4.6         | A bit tricky |
| 4.7         | Either.traverse() and sequence(). A bit tricky. I didn't see that map2 could be used to implement traverse. |

## Tips

1. Check pattern matches for redundant case statements i.e. where a more general case statement covers an earlier specific case.
2. When using foldRight, consider using foldLeft on the reversed list or a mutable local variable for greater efficiency and safety against stack overflows.
   _Edit: Not necessary... The [Scala 2.11.0 implementation](https://github.com/scala/scala/blob/v2.11.0/src/library/scala/collection/immutable/List.scala#L399) does this already_
3. Nil: List[B] is necessary in fold calls to bind the return type. Just Nil is probably sufficient in the implementation as the type is already bound.
4. In pattern matches, see if there is a way to make the last clause a "case _ => ...", as this ensures that all scenarios have been covered.
5. See opportunities for using map or flatMap on an option by looking for pattern match expression where None maps to None, and Some(?) maps to an expression. If the result is a Some(expr) convert to map(expr). If the result can evaluate to Some or None, then use flatMap.
6. In exercise 4.6, Left[E] and Right[A] only implement one type parameter, so one must typecast to Either[E,A] to get correct signature for map, flatMap, etc.
7. In 4.7, "(_)" didn't work for an identity map. I thought it would be necessary to specify the type of the parameter. But "(x => x)" would have worked.


## Thoughts

1. It sucks that Scala doesn't have automatic currying and type inference over all parameters. Multiple parameter lists is a clever but kludgy idea.
2. I don't have a mental model for when (_) will work as an identity map, and when (x => x) is required (see exercise 4.7). TODO: Update when I understand.
3. The Either data type feels very wrong to me (the names, not the concept):
  a. It looks symmetrical, but isn't. The left side short-circuits but the right side doesn't.
  b. The code misses an opportunity to be self-documenting. 
     The names don't reveal the intention, which is that one side should represent success and the other failure.
     As a result additional explanation is needed to explain that you should remember that "right" can also mean "correct".
  c. Even if you argue that general purpose names don't limit the use, there should still be a clue in the names that one side will short-circuit and the other not e.g. shortened and normal (short would be better, but Short is a data type).

{'fraction_non_alphanumeric': 0.08690869086908691, 'fraction_numerical': 0.022763814843022763, 'mean_word_length': 3.4512241054613937, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 19, 'https://': 4, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '584955', 'n_tokens_mistral': 3506, 'n_tokens_neox': 3256, 'n_words': 1736}