Skip to content

Commit 5071b5a

Browse files
committed
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](DrBoolean/RecursionTalk#2 (comment)) and tweaked a few wording. Also cc @joneshf @getify @shineli1984 Thanks!
1 parent 273175d commit 5071b5a

File tree

1 file changed

+73
-5
lines changed

1 file changed

+73
-5
lines changed

readme.md

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ __Table of Contents__
5151
* [Morphism](#morphism)
5252
* [Endomorphism](#endomorphism)
5353
* [Isomorphism](#isomorphism)
54+
* [Catamorphism](#catamorphism)
55+
* [Anamorphism](#anamorphism)
56+
* [Hylomorphism](#hylomorphism)
57+
* [Paramorphism](#paramorphism)
58+
* [Apomorphism](#apomorphism)
5459
* [Setoid](#setoid)
5560
* [Semigroup](#semigroup)
5661
* [Foldable](#foldable)
@@ -146,10 +151,10 @@ add2(10) // 12
146151
## Closure
147152

148153
A closure is a way of accessing a variable outside its scope.
149-
Formally, a closure is a technique for implementing lexically scopped named binding. It is a way of storing a function with an environment.
154+
Formally, a closure is a technique for implementing lexically scopped named binding. It is a way of storing a function with an environment.
150155

151156
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.
152-
ie. they allow referencing a scope after the block in which the variables were declared has finished executing.
157+
ie. they allow referencing a scope after the block in which the variables were declared has finished executing.
153158

154159

155160
```js
@@ -159,15 +164,15 @@ addToFive(3); //returns 8
159164
```
160165
The function ```addTo()``` returns a function(internally called ```add()```), lets store it in a variable called ```addToFive``` with a curried call having parameter 5.
161166

162-
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.
167+
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.
163168

164169
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.
165170

166171
The stack along with the lexical scope of the function is stored in form of reference to the parent. This prevents the closure and the underlying variables from being garbage collected(since there is at least one live reference to it).
167172

168173
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.
169174

170-
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.
175+
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.
171176

172177

173178
__Further reading/Sources__
@@ -352,7 +357,7 @@ TODO
352357
## Category
353358

354359
A category in category theory is a collection of objects and morphisms between them. In programming, typically types
355-
act as the objects and functions as morphisms.
360+
act as the objects and functions as morphisms.
356361

357362
To be a valid category 3 rules must be met:
358363

@@ -691,7 +696,70 @@ coordsToPair(pairToCoords([1, 2])) // [1, 2]
691696
pairToCoords(coordsToPair({x: 1, y: 2})) // {x: 1, y: 2}
692697
```
693698

699+
### Catamorphism
694700

701+
A `reduceRight` function. Take a bunch of things, and combine them into another. The morphism is from "bunch of things" to "another".
702+
703+
```js
704+
const sum = xs => xs.reduceRight((acc, x) => acc + x, 0)
705+
706+
sum([1, 2, 3, 4, 5]) // 15
707+
```
708+
709+
### Anamorphism
710+
711+
An `unfold` function. An `unfold` is the opposite of `fold` (`reduce`). It generates a list from a single value.
712+
713+
```js
714+
const unfold = (f, seed) => {
715+
function go(f, seed, acc) {
716+
const res = f(seed);
717+
return res ? go(f, res[1], acc.concat([res[0]])) : acc;
718+
}
719+
return go(f, seed, [])
720+
}
721+
```
722+
723+
```js
724+
const countDown = n => unfold((n) => {
725+
return n <= 0 ? undefined : [n, n - 1]
726+
}, n)
727+
728+
countDown(5) // [5, 4, 3, 2, 1]
729+
```
730+
731+
### Hylomorphism
732+
733+
The combination of anamorphism and catamorphism.
734+
735+
### Paramorphism
736+
737+
Enhancement of catamorphism. It's like `reduceRight`. However, there's a difference:
738+
739+
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.
740+
741+
```js
742+
// Obviously not safe for lists containing `undefined`,
743+
// but good enough to make the point.
744+
const para = (reducer, accumulator, [x, ... xs]) =>
745+
x !== undefined
746+
? reducer(x, xs, para(reducer, accumulator, xs))
747+
: accumulator
748+
749+
const suffixes = list => para(
750+
(x, xs, suffxs) => [xs, ... suffxs],
751+
[],
752+
list
753+
)
754+
755+
suffixes([1, 2, 3, 4, 5]) // [[2, 3, 4, 5], [3, 4, 5], [4, 5], [5], []]
756+
```
757+
758+
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.
759+
760+
### Apomorphism
761+
762+
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.
695763

696764
## Setoid
697765

0 commit comments

Comments
 (0)