From 5071b5a617282d77edff3816c7951135f1ac4b70 Mon Sep 17 00:00:00 2001 From: Steve Mao Date: Mon, 10 Apr 2017 20:16:44 +1000 Subject: [PATCH 1/4] Add more morphisms I was watching @DrBoolean's [A Million Ways to Fold in JS](https://www.youtube.com/watch?v=JZSoPZUoR58) but I couldn't understand most of the morphism jargons. I presume the video is for experienced devs who are from a FP language background. The moment when I tried to search for "Catamorphism javascript" on google I couldn't get anything. I really hope there would be more in depth FP resources written in JavaScript. Luckily @i-am-tom kindly wrote up something that could be understood by JS devs like me. I have fixed a minor mistake in @i-am-tom's [original write up](https://github.com/DrBoolean/RecursionTalk/issues/2#issuecomment-292777148) and tweaked a few wording. Also cc @joneshf @getify @shineli1984 Thanks! --- readme.md | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 73 insertions(+), 5 deletions(-) diff --git a/readme.md b/readme.md index 34faf12..187cee3 100644 --- a/readme.md +++ b/readme.md @@ -51,6 +51,11 @@ __Table of Contents__ * [Morphism](#morphism) * [Endomorphism](#endomorphism) * [Isomorphism](#isomorphism) + * [Catamorphism](#catamorphism) + * [Anamorphism](#anamorphism) + * [Hylomorphism](#hylomorphism) + * [Paramorphism](#paramorphism) + * [Apomorphism](#apomorphism) * [Setoid](#setoid) * [Semigroup](#semigroup) * [Foldable](#foldable) @@ -146,10 +151,10 @@ add2(10) // 12 ## Closure A closure is a way of accessing a variable outside its scope. -Formally, a closure is a technique for implementing lexically scopped named binding. It is a way of storing a function with an environment. +Formally, a closure is a technique for implementing lexically scopped named binding. It is a way of storing a function with an environment. A closure is a scope which captures local variables of a function for access even after the execution has moved out of the block in which it is defined. -ie. they allow referencing a scope after the block in which the variables were declared has finished executing. +ie. they allow referencing a scope after the block in which the variables were declared has finished executing. ```js @@ -159,7 +164,7 @@ addToFive(3); //returns 8 ``` The function ```addTo()``` returns a function(internally called ```add()```), lets store it in a variable called ```addToFive``` with a curried call having parameter 5. -Ideally, when the function ```addTo``` finishes execution, its scope, with local variables add, x, y should not be accessible. But, it returns 8 on calling ```addToFive()```. This means that the state of the function ```addTo``` is saved even after the block of code has finished executing, otherwise there is no way of knowing that ```addTo``` was called as ```addTo(5)``` and the value of x was set to 5. +Ideally, when the function ```addTo``` finishes execution, its scope, with local variables add, x, y should not be accessible. But, it returns 8 on calling ```addToFive()```. This means that the state of the function ```addTo``` is saved even after the block of code has finished executing, otherwise there is no way of knowing that ```addTo``` was called as ```addTo(5)``` and the value of x was set to 5. Lexical scoping is the reason why it is able to find the values of x and add - the private variables of the parent which has finished executing. This value is called a Closure. @@ -167,7 +172,7 @@ The stack along with the lexical scope of the function is stored in form of refe Lambda Vs Closure: A lambda is essentially a function that is defined inline rather than the standard method of declaring functions. Lambdas can frequently be passed around as objects. -A closure is a function that encloses its surrounding state by referencing fields external to its body. The enclosed state remains across invocations of the closure. +A closure is a function that encloses its surrounding state by referencing fields external to its body. The enclosed state remains across invocations of the closure. __Further reading/Sources__ @@ -352,7 +357,7 @@ TODO ## Category A category in category theory is a collection of objects and morphisms between them. In programming, typically types -act as the objects and functions as morphisms. +act as the objects and functions as morphisms. To be a valid category 3 rules must be met: @@ -691,7 +696,70 @@ coordsToPair(pairToCoords([1, 2])) // [1, 2] pairToCoords(coordsToPair({x: 1, y: 2})) // {x: 1, y: 2} ``` +### Catamorphism +A `reduceRight` function. Take a bunch of things, and combine them into another. The morphism is from "bunch of things" to "another". + +```js +const sum = xs => xs.reduceRight((acc, x) => acc + x, 0) + +sum([1, 2, 3, 4, 5]) // 15 +``` + +### Anamorphism + +An `unfold` function. An `unfold` is the opposite of `fold` (`reduce`). It generates a list from a single value. + +```js +const unfold = (f, seed) => { + function go(f, seed, acc) { + const res = f(seed); + return res ? go(f, res[1], acc.concat([res[0]])) : acc; + } + return go(f, seed, []) +} +``` + +```js +const countDown = n => unfold((n) => { + return n <= 0 ? undefined : [n, n - 1] +}, n) + +countDown(5) // [5, 4, 3, 2, 1] +``` + +### Hylomorphism + +The combination of anamorphism and catamorphism. + +### Paramorphism + +Enhancement of catamorphism. It's like `reduceRight`. However, there's a difference: + +In paramorphism, your reducer's arguments are the current value, the reduction of all previous values, and the list of values that formed that reduction. + +```js +// Obviously not safe for lists containing `undefined`, +// but good enough to make the point. +const para = (reducer, accumulator, [x, ... xs]) => + x !== undefined + ? reducer(x, xs, para(reducer, accumulator, xs)) + : accumulator + +const suffixes = list => para( + (x, xs, suffxs) => [xs, ... suffxs], + [], + list +) + +suffixes([1, 2, 3, 4, 5]) // [[2, 3, 4, 5], [3, 4, 5], [4, 5], [5], []] +``` + +The third parameter in the reducer (in the above example, `[x, ... xs]`) is kind of like having a history of what got you to your current acc value. + +### Apomorphism + +it's the opposite of paramorphism, just as anamorphism is the opposite of catamorphism. Whereas with paramorphism, you combine with access to the accumulator and what has been accumulated, apomorphism lets you `unfold` with the potential to return early. ## Setoid From c39c733ff6d4d19693a9c159fd4ebb8404bee4c2 Mon Sep 17 00:00:00 2001 From: Steve Mao Date: Mon, 10 Apr 2017 21:08:44 +1000 Subject: [PATCH 2/4] Address @i-am-tom and @shineli1984's review --- readme.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/readme.md b/readme.md index 187cee3..fdddfd9 100644 --- a/readme.md +++ b/readme.md @@ -698,7 +698,7 @@ pairToCoords(coordsToPair({x: 1, y: 2})) // {x: 1, y: 2} ### Catamorphism -A `reduceRight` function. Take a bunch of things, and combine them into another. The morphism is from "bunch of things" to "another". +A `reduceRight` function that applies a function against an accumulator and each value of the array (from right-to-left) to reduce it to a single value. ```js const sum = xs => xs.reduceRight((acc, x) => acc + x, 0) @@ -734,17 +734,22 @@ The combination of anamorphism and catamorphism. ### Paramorphism -Enhancement of catamorphism. It's like `reduceRight`. However, there's a difference: +A function just like `reduceRight`. However, there's a difference: In paramorphism, your reducer's arguments are the current value, the reduction of all previous values, and the list of values that formed that reduction. ```js // Obviously not safe for lists containing `undefined`, // but good enough to make the point. -const para = (reducer, accumulator, [x, ... xs]) => - x !== undefined - ? reducer(x, xs, para(reducer, accumulator, xs)) - : accumulator +const para = (reducer, accumulator, elements) => { + if (elements.length === 0) + return accumulator + + const head = elements[0] + const tail = elements.slice(1) + + return reducer(head, tail, para(reducer, accumulator, tail)) +} const suffixes = list => para( (x, xs, suffxs) => [xs, ... suffxs], From 72fe6956aae6943a476a5974d8a78caed829c3c9 Mon Sep 17 00:00:00 2001 From: Steve Mao Date: Mon, 10 Apr 2017 21:10:56 +1000 Subject: [PATCH 3/4] Since the definition of `reduceRight` wan't correct, the definition of `reduce` doesn't seem to be right either. Changed both to mdn's definition. --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index fdddfd9..7e1e235 100644 --- a/readme.md +++ b/readme.md @@ -800,7 +800,7 @@ An object that has a `concat` function that combines it with another object of t ## Foldable -An object that has a `reduce` function that can transform that object into some other type. +An object that has a `reduce` function that applies a function against an accumulator and each element in the array (from left to right) to reduce it to a single value. ```js const sum = (list) => list.reduce((acc, val) => acc + val, 0) From 1d15590ebac8357e5e250df373801158860feb01 Mon Sep 17 00:00:00 2001 From: Steve Mao Date: Sun, 16 Apr 2017 09:06:54 +1000 Subject: [PATCH 4/4] add Homomorphism --- readme.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/readme.md b/readme.md index 7e1e235..8258be0 100644 --- a/readme.md +++ b/readme.md @@ -51,6 +51,7 @@ __Table of Contents__ * [Morphism](#morphism) * [Endomorphism](#endomorphism) * [Isomorphism](#isomorphism) + * [Homomorphism](#homomorphism) * [Catamorphism](#catamorphism) * [Anamorphism](#anamorphism) * [Hylomorphism](#hylomorphism) @@ -696,6 +697,16 @@ coordsToPair(pairToCoords([1, 2])) // [1, 2] pairToCoords(coordsToPair({x: 1, y: 2})) // {x: 1, y: 2} ``` +### Homomorphism + +A homomorphism is just a structure preserving map. In fact, a functor is just a homomorphism between categories as it preserves the original category's structure under the mapping. + +```js +A.of(f).ap(A.of(x)) == A.of(f(x)) + +Either.of(_.toUpper).ap(Either.of("oreos")) == Either.of(_.toUpper("oreos")) +``` + ### Catamorphism A `reduceRight` function that applies a function against an accumulator and each value of the array (from right-to-left) to reduce it to a single value.