mirror of
https://github.com/rjNemo/functional-programming-jargon
synced 2026-06-11 04:56:40 +00:00
This commit is contained in:
parent
6256c79347
commit
a67155dd44
4 changed files with 161 additions and 139 deletions
7
.eslintrc.yml
Normal file
7
.eslintrc.yml
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
---
|
||||||
|
extends: "standard"
|
||||||
|
plugins: [markdown]
|
||||||
|
rules:
|
||||||
|
no-unused-vars: 0
|
||||||
|
no-undef: 0
|
||||||
|
no-extend-native: 0
|
||||||
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
This project is a work in progress. Contributions are very welcome.
|
This project is a work in progress. Contributions are very welcome.
|
||||||
|
|
||||||
|
## Hard rules
|
||||||
|
* Run `npm test` to lint the code examples. Your changes must pass.
|
||||||
|
* If you add a new definition or reorder them run `npm run toc` to regenerate the table of contents.
|
||||||
|
|
||||||
That said, we'd like to maintain some consistency across the document.
|
That said, we'd like to maintain some consistency across the document.
|
||||||
|
|
||||||
## Style guide
|
## Style guide
|
||||||
|
|
@ -14,12 +18,12 @@ That said, we'd like to maintain some consistency across the document.
|
||||||
1. Avoid big walls of text
|
1. Avoid big walls of text
|
||||||
|
|
||||||
## Code conventions
|
## Code conventions
|
||||||
Be consistent with other examples
|
[](https://github.com/feross/standard)
|
||||||
|
|
||||||
|
* Be consistent with other examples
|
||||||
* Prefer arrow functions
|
* Prefer arrow functions
|
||||||
* Parenthesis around function arguments
|
* Parenthesis around function arguments
|
||||||
* Put output values in comments
|
* Put output values in comments
|
||||||
* Use semi-colons
|
|
||||||
* Keep it short and simple
|
* Keep it short and simple
|
||||||
|
|
||||||
This styleguide is a WIP too! Send PRs :)
|
This styleguide is a WIP too! Send PRs :)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
"description": "Jargon from the functional programming world in simple terms!",
|
"description": "Jargon from the functional programming world in simple terms!",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "eslint readme.md",
|
||||||
"toc": "roadmarks"
|
"toc": "roadmarks"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
|
|
@ -18,6 +18,9 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/hemanth/functional-programming-jargon#readme",
|
"homepage": "https://github.com/hemanth/functional-programming-jargon#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"eslint": "^3.4.0",
|
||||||
|
"eslint-config-standard": "^6.0.0",
|
||||||
|
"eslint-plugin-markdown": "^1.0.0-beta.2",
|
||||||
"roadmarks": "^1.6.3"
|
"roadmarks": "^1.6.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
280
readme.md
280
readme.md
|
|
@ -63,10 +63,10 @@ __Table of Contents__
|
||||||
The number of arguments a function takes. From words like unary, binary, ternary, etc. This word has the distinction of being composed of two suffixes, "-ary" and "-ity." Addition, for example, takes two arguments, and so it is defined as a binary function or a function with an arity of two. Such a function may sometimes be called "dyadic" by people who prefer Greek roots to Latin. Likewise, a function that takes a variable number of arguments is called "variadic," whereas a binary function must be given two and only two arguments, currying and partial application notwithstanding (see below).
|
The number of arguments a function takes. From words like unary, binary, ternary, etc. This word has the distinction of being composed of two suffixes, "-ary" and "-ity." Addition, for example, takes two arguments, and so it is defined as a binary function or a function with an arity of two. Such a function may sometimes be called "dyadic" by people who prefer Greek roots to Latin. Likewise, a function that takes a variable number of arguments is called "variadic," whereas a binary function must be given two and only two arguments, currying and partial application notwithstanding (see below).
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const sum = (a, b) => a + b;
|
const sum = (a, b) => a + b
|
||||||
|
|
||||||
const arity = sum.length;
|
const arity = sum.length
|
||||||
console.log(arity); // 2
|
console.log(arity) // 2
|
||||||
|
|
||||||
// The arity of sum is 2
|
// The arity of sum is 2
|
||||||
```
|
```
|
||||||
|
|
@ -77,22 +77,22 @@ A function which takes a function as an argument and/or returns a function.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const filter = (predicate, xs) => {
|
const filter = (predicate, xs) => {
|
||||||
const result = [];
|
const result = []
|
||||||
for (let idx = 0; idx < xs.length; idx++) {
|
for (let idx = 0; idx < xs.length; idx++) {
|
||||||
if (predicate(xs[idx])) {
|
if (predicate(xs[idx])) {
|
||||||
result.push(xs[idx]);
|
result.push(xs[idx])
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
}
|
||||||
};
|
return result
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const is = (type) => (x) => Object(x) instanceof type;
|
const is = (type) => (x) => Object(x) instanceof type
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
filter(is(Number), [0, '1', 2, null]); // [0, 2]
|
filter(is(Number), [0, '1', 2, null]) // [0, 2]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Partial Application
|
## Partial Application
|
||||||
|
|
@ -104,24 +104,24 @@ Partially applying a function means creating a new function by pre-filling some
|
||||||
// Helper to create partially applied functions
|
// Helper to create partially applied functions
|
||||||
// Takes a function and some arguments
|
// Takes a function and some arguments
|
||||||
const partial = (f, ...args) =>
|
const partial = (f, ...args) =>
|
||||||
// returns a function that takes the rest of the arguments
|
// returns a function that takes the rest of the arguments
|
||||||
(...moreArgs) =>
|
(...moreArgs) =>
|
||||||
// and calls the original function with all of them
|
// and calls the original function with all of them
|
||||||
f(...args, ...moreArgs);
|
f(...args, ...moreArgs)
|
||||||
|
|
||||||
// Something to apply
|
// Something to apply
|
||||||
const add3 = (a, b, c) => a + b + c;
|
const add3 = (a, b, c) => a + b + c
|
||||||
|
|
||||||
// Partially applying `2` and `3` to `add3` gives you a one-argument function
|
// Partially applying `2` and `3` to `add3` gives you a one-argument function
|
||||||
const fivePlus = partial(add3, 2, 3); // (c) => 2 + 3 + c
|
const fivePlus = partial(add3, 2, 3) // (c) => 2 + 3 + c
|
||||||
|
|
||||||
fivePlus(4); // 9
|
fivePlus(4) // 9
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also use `Function.prototype.bind` to partially apply a function in JS:
|
You can also use `Function.prototype.bind` to partially apply a function in JS:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const add1More = add3.bind(null, 2, 3); // (c) => 2 + 3 + c
|
const add1More = add3.bind(null, 2, 3) // (c) => 2 + 3 + c
|
||||||
```
|
```
|
||||||
|
|
||||||
Partial application helps create simpler functions from more complex ones by baking in data when you have it. [Curried](#currying) functions are automatically partially applied.
|
Partial application helps create simpler functions from more complex ones by baking in data when you have it. [Curried](#currying) functions are automatically partially applied.
|
||||||
|
|
@ -133,13 +133,13 @@ The process of converting a function that takes multiple arguments into a functi
|
||||||
Each time the function is called it only accepts one argument and returns a function that takes one argument until all arguments are passed.
|
Each time the function is called it only accepts one argument and returns a function that takes one argument until all arguments are passed.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const sum = (a, b) => a + b;
|
const sum = (a, b) => a + b
|
||||||
|
|
||||||
const curriedSum = (a) => (b) => a + b;
|
const curriedSum = (a) => (b) => a + b
|
||||||
|
|
||||||
curriedSum(40)(2) // 42.
|
curriedSum(40)(2) // 42.
|
||||||
|
|
||||||
const add2 = curriedSum(2); // (b) => 2 + b
|
const add2 = curriedSum(2) // (b) => 2 + b
|
||||||
|
|
||||||
add2(10) // 12
|
add2(10) // 12
|
||||||
|
|
||||||
|
|
@ -151,9 +151,9 @@ Transforming a function that takes multiple arguments into one that if given les
|
||||||
Underscore, lodash, and ramda have a `curry` function that works this way.
|
Underscore, lodash, and ramda have a `curry` function that works this way.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const add = (x, y) => x + y;
|
const add = (x, y) => x + y
|
||||||
|
|
||||||
const curriedAdd = _.curry(add);
|
const curriedAdd = _.curry(add)
|
||||||
curriedAdd(1, 2) // 3
|
curriedAdd(1, 2) // 3
|
||||||
curriedAdd(1) // (y) => 1 + y
|
curriedAdd(1) // (y) => 1 + y
|
||||||
curriedAdd(1)(2) // 3
|
curriedAdd(1)(2) // 3
|
||||||
|
|
@ -170,7 +170,7 @@ The act of putting two functions together to form a third function where the out
|
||||||
```js
|
```js
|
||||||
const compose = (f, g) => (a) => f(g(a)) // Definition
|
const compose = (f, g) => (a) => f(g(a)) // Definition
|
||||||
const floorAndToString = compose((val) => val.toString(), Math.floor) // Usage
|
const floorAndToString = compose((val) => val.toString(), Math.floor) // Usage
|
||||||
floorAndToString(121.212121) // "121"
|
floorAndToString(121.212121) // '121'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Purity
|
## Purity
|
||||||
|
|
@ -179,9 +179,9 @@ A function is pure if the return value is only determined by its
|
||||||
input values, and does not produce side effects.
|
input values, and does not produce side effects.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const greet = (name) => "Hi, " + name ;
|
const greet = (name) => 'Hi, ' + name
|
||||||
|
|
||||||
greet("Brianne") // "Hi, Brianne"
|
greet('Brianne') // 'Hi, Brianne'
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -189,11 +189,13 @@ As opposed to:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
let greeting;
|
let greeting
|
||||||
|
|
||||||
const greet = () => greeting = "Hi, " + window.name;
|
const greet = () => {
|
||||||
|
greeting = 'Hi, ' + window.name
|
||||||
|
}
|
||||||
|
|
||||||
greet(); // "Hi, Brianne"
|
greet() // "Hi, Brianne"
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -202,19 +204,19 @@ greet(); // "Hi, Brianne"
|
||||||
A function or expression is said to have a side effect if apart from returning a value, it interacts with (reads from or writes to) external mutable state.
|
A function or expression is said to have a side effect if apart from returning a value, it interacts with (reads from or writes to) external mutable state.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const differentEveryTime = new Date();
|
const differentEveryTime = new Date()
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
console.log("IO is a side effect!");
|
console.log('IO is a side effect!')
|
||||||
```
|
```
|
||||||
|
|
||||||
## Idempotent
|
## Idempotent
|
||||||
|
|
||||||
A function is idempotent if reapplying it to its result does not produce a different result.
|
A function is idempotent if reapplying it to its result does not produce a different result.
|
||||||
|
|
||||||
```js
|
```
|
||||||
f(f(x)) = f(x)
|
f(f(x)) ≍ f(x)
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
@ -222,7 +224,7 @@ Math.abs(Math.abs(10))
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
sort(sort(sort([2,1])))
|
sort(sort(sort([2, 1])))
|
||||||
```
|
```
|
||||||
|
|
||||||
## Point-Free Style
|
## Point-Free Style
|
||||||
|
|
@ -231,16 +233,16 @@ Writing functions where the definition does not explicitly identify the argument
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Given
|
// Given
|
||||||
const map = (fn) => (list) => list.map(fn);
|
const map = (fn) => (list) => list.map(fn)
|
||||||
const add = (a) => (b) => a + b;
|
const add = (a) => (b) => a + b
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
|
|
||||||
// Not points-free - `numbers` is an explicit argument
|
// Not points-free - `numbers` is an explicit argument
|
||||||
const incrementAll = (numbers) => map(add(1))(numbers);
|
const incrementAll = (numbers) => map(add(1))(numbers)
|
||||||
|
|
||||||
// Points-free - The list is an implicit argument
|
// Points-free - The list is an implicit argument
|
||||||
const incrementAll2 = map(add(1));
|
const incrementAll2 = map(add(1))
|
||||||
```
|
```
|
||||||
|
|
||||||
`incrementAll` identifies and uses the parameter `numbers`, so it is not points-free. `incrementAll2` is written just by combining functions and values, making no mention of its arguments. It __is__ points-free.
|
`incrementAll` identifies and uses the parameter `numbers`, so it is not points-free. `incrementAll2` is written just by combining functions and values, making no mention of its arguments. It __is__ points-free.
|
||||||
|
|
@ -251,9 +253,9 @@ Points-free function definitions look just like normal assignments without `func
|
||||||
A predicate is a function that returns true or false for a given value. A common use of a predicate is as the callback for array filter.
|
A predicate is a function that returns true or false for a given value. A common use of a predicate is as the callback for array filter.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const predicate = (a) => a > 2;
|
const predicate = (a) => a > 2
|
||||||
|
|
||||||
[1, 2, 3, 4].filter(predicate); // [3, 4]
|
;[1, 2, 3, 4].filter(predicate) // [3, 4]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Contracts
|
## Contracts
|
||||||
|
|
@ -275,8 +277,8 @@ Anything that can be assigned to a variable.
|
||||||
```js
|
```js
|
||||||
5
|
5
|
||||||
Object.freeze({name: 'John', age: 30}) // The `freeze` function enforces immutability.
|
Object.freeze({name: 'John', age: 30}) // The `freeze` function enforces immutability.
|
||||||
(a) => a
|
;(a) => a
|
||||||
[1]
|
;[1]
|
||||||
undefined
|
undefined
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -318,17 +320,17 @@ object.map(x => f(g(x))) === object.map(g).map(f)
|
||||||
A common functor in JavaScript is `Array` since it abides to the two functor rules:
|
A common functor in JavaScript is `Array` since it abides to the two functor rules:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
[1, 2, 3].map(x => x); // = [1, 2, 3]
|
[1, 2, 3].map(x => x) // = [1, 2, 3]
|
||||||
```
|
```
|
||||||
|
|
||||||
and
|
and
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const f = x => x + 1;
|
const f = x => x + 1
|
||||||
const g = x => x * 2;
|
const g = x => x * 2
|
||||||
|
|
||||||
[1, 2, 3].map(x => f(g(x))); // = [3, 5, 7]
|
;[1, 2, 3].map(x => f(g(x))) // = [3, 5, 7]
|
||||||
[1, 2, 3].map(g).map(f); // = [3, 5, 7]
|
;[1, 2, 3].map(g).map(f) // = [3, 5, 7]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Pointed Functor
|
## Pointed Functor
|
||||||
|
|
@ -347,23 +349,23 @@ Lifting is when you take a value and put it into an object like a [functor](#poi
|
||||||
Some implementations have a function called `lift`, or `liftA2` to make it easier to run functions on functors.
|
Some implementations have a function called `lift`, or `liftA2` to make it easier to run functions on functors.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const liftA2 = (f) => (a, b) => a.map(f).ap(b);
|
const liftA2 = (f) => (a, b) => a.map(f).ap(b)
|
||||||
|
|
||||||
const mult = a => b => a * b;
|
const mult = a => b => a * b
|
||||||
|
|
||||||
const liftedMult = liftA2(mult); // this function now works on functors like array
|
const liftedMult = liftA2(mult) // this function now works on functors like array
|
||||||
|
|
||||||
liftedMult([1, 2], [3]); // [3, 6]
|
liftedMult([1, 2], [3]) // [3, 6]
|
||||||
liftA2((a, b) => a + b)([1, 2], [3, 4]); // [4, 5, 5, 6]
|
liftA2((a, b) => a + b)([1, 2], [3, 4]) // [4, 5, 5, 6]
|
||||||
```
|
```
|
||||||
|
|
||||||
Lifting a one-argument function and applying it does the same thing as `map`.
|
Lifting a one-argument function and applying it does the same thing as `map`.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const increment = (x) => x + 1;
|
const increment = (x) => x + 1
|
||||||
|
|
||||||
lift(increment)([2]); // [3]
|
lift(increment)([2]) // [3]
|
||||||
[2].map(increment); // [3]
|
;[2].map(increment) // [3]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -375,7 +377,7 @@ behavior of the program is said to be referentially transparent.
|
||||||
Say we have function greet:
|
Say we have function greet:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const greet = () => "Hello World!";
|
const greet = () => 'Hello World!'
|
||||||
```
|
```
|
||||||
|
|
||||||
Any invocation of `greet()` can be replaced with `Hello World!` hence greet is
|
Any invocation of `greet()` can be replaced with `Hello World!` hence greet is
|
||||||
|
|
@ -390,22 +392,22 @@ When an application is composed of expressions and devoid of side effects, truth
|
||||||
An anonymous function that can be treated like a value.
|
An anonymous function that can be treated like a value.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
function(a){
|
;(function (a) {
|
||||||
return a + 1;
|
return a + 1
|
||||||
};
|
})
|
||||||
|
|
||||||
(a) => a + 1;
|
;(a) => a + 1
|
||||||
```
|
```
|
||||||
Lambdas are often passed as arguments to Higher-Order functions.
|
Lambdas are often passed as arguments to Higher-Order functions.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
[1, 2].map((a) => a + 1); // [2, 3]
|
[1, 2].map((a) => a + 1) // [2, 3]
|
||||||
```
|
```
|
||||||
|
|
||||||
You can assign a lambda to a variable.
|
You can assign a lambda to a variable.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const add1 = (a) => a + 1;
|
const add1 = (a) => a + 1
|
||||||
```
|
```
|
||||||
|
|
||||||
## Lambda Calculus
|
## Lambda Calculus
|
||||||
|
|
@ -417,15 +419,15 @@ Lazy evaluation is a call-by-need evaluation mechanism that delays the evaluatio
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const rand = function*() {
|
const rand = function*() {
|
||||||
while (1 < 2) {
|
while (1 < 2) {
|
||||||
yield Math.random();
|
yield Math.random()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const randIter = rand();
|
const randIter = rand()
|
||||||
randIter.next(); // Each execution gives a random value, expression is evaluated on need.
|
randIter.next() // Each execution gives a random value, expression is evaluated on need.
|
||||||
```
|
```
|
||||||
|
|
||||||
## Monoid
|
## Monoid
|
||||||
|
|
@ -435,7 +437,7 @@ An object with a function that "combines" that object with another of the same t
|
||||||
One simple monoid is the addition of numbers:
|
One simple monoid is the addition of numbers:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
1 + 1; // 2
|
1 + 1 // 2
|
||||||
```
|
```
|
||||||
In this case number is the object and `+` is the function.
|
In this case number is the object and `+` is the function.
|
||||||
|
|
||||||
|
|
@ -443,33 +445,35 @@ An "identity" value must also exist that when combined with a value doesn't chan
|
||||||
|
|
||||||
The identity value for addition is `0`.
|
The identity value for addition is `0`.
|
||||||
```js
|
```js
|
||||||
1 + 0; // 1
|
1 + 0 // 1
|
||||||
```
|
```
|
||||||
|
|
||||||
It's also required that the grouping of operations will not affect the result (associativity):
|
It's also required that the grouping of operations will not affect the result (associativity):
|
||||||
|
|
||||||
```js
|
```js
|
||||||
1 + (2 + 3) === (1 + 2) + 3; // true
|
1 + (2 + 3) === (1 + 2) + 3 // true
|
||||||
```
|
```
|
||||||
|
|
||||||
Array concatenation also forms a monoid:
|
Array concatenation also forms a monoid:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
[1, 2].concat([3, 4]); // [1, 2, 3, 4]
|
;[1, 2].concat([3, 4]) // [1, 2, 3, 4]
|
||||||
```
|
```
|
||||||
|
|
||||||
The identity value is empty array `[]`
|
The identity value is empty array `[]`
|
||||||
|
|
||||||
```js
|
```js
|
||||||
[1, 2].concat([]); // [1, 2]
|
;[1, 2].concat([]) // [1, 2]
|
||||||
```
|
```
|
||||||
|
|
||||||
If identity and compose functions are provided, functions themselves form a monoid:
|
If identity and compose functions are provided, functions themselves form a monoid:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const identity = (a) => a;
|
const identity = (a) => a
|
||||||
const compose = (f, g) => (x) => f(g(x));
|
const compose = (f, g) => (x) => f(g(x))
|
||||||
|
```
|
||||||
|
`foo` is any function that takes one argument.
|
||||||
|
```
|
||||||
compose(foo, identity) ≍ compose(identity, foo) ≍ foo
|
compose(foo, identity) ≍ compose(identity, foo) ≍ foo
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -479,15 +483,15 @@ A monad is an object with [`of`](#pointed-functor) and `chain` functions. `chain
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Implementation
|
// Implementation
|
||||||
Array.prototype.chain = function(f){
|
Array.prototype.chain = function (f) {
|
||||||
return this.reduce((acc, it) => acc.concat(f(it)), []);
|
return this.reduce((acc, it) => acc.concat(f(it)), [])
|
||||||
};
|
}
|
||||||
|
|
||||||
// Usage
|
// Usage
|
||||||
['cat,dog', 'fish,bird'].chain((a) => a.split(',')) // ['cat', 'dog', 'fish', 'bird']
|
;['cat,dog', 'fish,bird'].chain((a) => a.split(',')) // ['cat', 'dog', 'fish', 'bird']
|
||||||
|
|
||||||
// Contrast to map
|
// Contrast to map
|
||||||
['cat,dog', 'fish,bird'].map((a) => a.split(',')) // [['cat', 'dog'], ['fish', 'bird']]
|
;['cat,dog', 'fish,bird'].map((a) => a.split(',')) // [['cat', 'dog'], ['fish', 'bird']]
|
||||||
```
|
```
|
||||||
|
|
||||||
`of` is also known as `return` in other functional languages.
|
`of` is also known as `return` in other functional languages.
|
||||||
|
|
@ -499,9 +503,13 @@ An object that has `extract` and `extend` functions.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const CoIdentity = (v) => ({
|
const CoIdentity = (v) => ({
|
||||||
val: v,
|
val: v,
|
||||||
extract() { return this.val },
|
extract () {
|
||||||
extend(f) { return CoIdentity(f(this)) }
|
return this.val
|
||||||
|
},
|
||||||
|
extend (f) {
|
||||||
|
return CoIdentity(f(this))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -523,31 +531,31 @@ An applicative functor is an object with an `ap` function. `ap` applies a functi
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Implementation
|
// Implementation
|
||||||
Array.prototype.ap = function(xs){
|
Array.prototype.ap = function (xs) {
|
||||||
return this.reduce((acc, f) => acc.concat(xs.map(f)), []);
|
return this.reduce((acc, f) => acc.concat(xs.map(f)), [])
|
||||||
};
|
}
|
||||||
|
|
||||||
// Example usage
|
// Example usage
|
||||||
[(a) => a + 1].ap([1]) // [2]
|
;[(a) => a + 1].ap([1]) // [2]
|
||||||
```
|
```
|
||||||
|
|
||||||
This is useful if you have two objects and you want to apply a binary function to their contents.
|
This is useful if you have two objects and you want to apply a binary function to their contents.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// Arrays that you want to combine
|
// Arrays that you want to combine
|
||||||
const arg1 = [1, 3];
|
const arg1 = [1, 3]
|
||||||
const arg2 = [4, 5];
|
const arg2 = [4, 5]
|
||||||
|
|
||||||
// combining function - must be curried for this to work
|
// combining function - must be curried for this to work
|
||||||
const add = (x) => (y) => x + y;
|
const add = (x) => (y) => x + y
|
||||||
|
|
||||||
const partiallyAppliedAdds = [add].ap(arg1); // [(y) => 1 + y, (y) => 3 + y]
|
const partiallyAppliedAdds = [add].ap(arg1) // [(y) => 1 + y, (y) => 3 + y]
|
||||||
```
|
```
|
||||||
|
|
||||||
This gives you an array of functions that you can call `ap` on to get the result:
|
This gives you an array of functions that you can call `ap` on to get the result:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
partiallyAppliedAdds.ap(arg2); // [5, 6, 7, 8]
|
partiallyAppliedAdds.ap(arg2) // [5, 6, 7, 8]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Morphism
|
## Morphism
|
||||||
|
|
@ -560,10 +568,10 @@ A function where the input type is the same as the output.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// uppercase :: String -> String
|
// uppercase :: String -> String
|
||||||
const uppercase = (str) => str.toUpperCase();
|
const uppercase = (str) => str.toUpperCase()
|
||||||
|
|
||||||
// decrement :: Number -> Number
|
// decrement :: Number -> Number
|
||||||
const decrement = (x) => x - 1;
|
const decrement = (x) => x - 1
|
||||||
```
|
```
|
||||||
|
|
||||||
### Isomorphism
|
### Isomorphism
|
||||||
|
|
@ -593,20 +601,20 @@ Make array a setoid:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
Array.prototype.equals = (arr) => {
|
Array.prototype.equals = (arr) => {
|
||||||
const len = this.length
|
const len = this.length
|
||||||
if (len !== arr.length) {
|
if (len !== arr.length) {
|
||||||
return false
|
return false
|
||||||
|
}
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
if (this[i] !== arr[i]) {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
for (let i = 0; i < len; i++) {
|
}
|
||||||
if (this[i] !== arr[i]) {
|
return true
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[1, 2].equals([1, 2]) // true
|
;[1, 2].equals([1, 2]) // true
|
||||||
[1, 2].equals([0]) // false
|
;[1, 2].equals([0]) // false
|
||||||
```
|
```
|
||||||
|
|
||||||
## Semigroup
|
## Semigroup
|
||||||
|
|
@ -614,7 +622,7 @@ Array.prototype.equals = (arr) => {
|
||||||
An object that has a `concat` function that combines it with another object of the same type.
|
An object that has a `concat` function that combines it with another object of the same type.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
[1].concat([2]) // [1, 2]
|
;[1].concat([2]) // [1, 2]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Foldable
|
## Foldable
|
||||||
|
|
@ -622,7 +630,7 @@ An object that has a `concat` function that combines it with another object of t
|
||||||
An object that has a `reduce` function that can transform that object into some other type.
|
An object that has a `reduce` function that can transform that object into some other type.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const sum = (list) => list.reduce((acc, val) => acc + val, 0);
|
const sum = (list) => list.reduce((acc, val) => acc + val, 0)
|
||||||
sum([1, 2, 3]) // 6
|
sum([1, 2, 3]) // 6
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -674,11 +682,11 @@ The `+` operator in JS works on strings and numbers so we can use this new type
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// add :: (NumOrString, NumOrString) -> NumOrString
|
// add :: (NumOrString, NumOrString) -> NumOrString
|
||||||
const add = (a, b) => a + b;
|
const add = (a, b) => a + b
|
||||||
|
|
||||||
add(1, 2); // Returns number 3
|
add(1, 2) // Returns number 3
|
||||||
add('Foo', 2); // Returns string "Foo2"
|
add('Foo', 2) // Returns string "Foo2"
|
||||||
add('Foo', 'Bar'); // Returns string "FooBar"
|
add('Foo', 'Bar') // Returns string "FooBar"
|
||||||
```
|
```
|
||||||
|
|
||||||
Union types are also known as algebraic types, tagged unions, or sum types.
|
Union types are also known as algebraic types, tagged unions, or sum types.
|
||||||
|
|
@ -691,7 +699,7 @@ A **product** type combines types together in a way you're probably more familia
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// point :: (Number, Number) -> {x: Number, y: Number}
|
// point :: (Number, Number) -> {x: Number, y: Number}
|
||||||
const point = (x, y) => ({x: x, y: y});
|
const point = (x, y) => ({x: x, y: y})
|
||||||
```
|
```
|
||||||
It's called a product because the total possible values of the data structure is the product of the different values.
|
It's called a product because the total possible values of the data structure is the product of the different values.
|
||||||
|
|
||||||
|
|
@ -706,42 +714,42 @@ Option is useful for composing functions that might not return a value.
|
||||||
// Naive definition
|
// Naive definition
|
||||||
|
|
||||||
const Some = (v) => ({
|
const Some = (v) => ({
|
||||||
val: v,
|
val: v,
|
||||||
map(f) {
|
map (f) {
|
||||||
return Some(f(this.val));
|
return Some(f(this.val))
|
||||||
},
|
},
|
||||||
chain(f) {
|
chain (f) {
|
||||||
return f(this.val);
|
return f(this.val)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const None = () => ({
|
const None = () => ({
|
||||||
map(f){
|
map (f) {
|
||||||
return this;
|
return this
|
||||||
},
|
},
|
||||||
chain(f){
|
chain (f) {
|
||||||
return this;
|
return this
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
// maybeProp :: (String, {a}) -> Option a
|
// maybeProp :: (String, {a}) -> Option a
|
||||||
const maybeProp = (key, obj) => typeof obj[key] === 'undefined' ? None() : Some(obj[key]);
|
const maybeProp = (key, obj) => typeof obj[key] === 'undefined' ? None() : Some(obj[key])
|
||||||
```
|
```
|
||||||
Use `chain` to sequence functions that return `Option`s
|
Use `chain` to sequence functions that return `Option`s
|
||||||
```js
|
```js
|
||||||
|
|
||||||
// getItem :: Cart -> Option CartItem
|
// getItem :: Cart -> Option CartItem
|
||||||
const getItem = (cart) => maybeProp('item', cart);
|
const getItem = (cart) => maybeProp('item', cart)
|
||||||
|
|
||||||
// getPrice :: Item -> Option Number
|
// getPrice :: Item -> Option Number
|
||||||
const getPrice = (item) => maybeProp('price', item);
|
const getPrice = (item) => maybeProp('price', item)
|
||||||
|
|
||||||
// getNestedPrice :: cart -> Option a
|
// getNestedPrice :: cart -> Option a
|
||||||
const getNestedPrice = (cart) => getItem(obj).chain(getPrice);
|
const getNestedPrice = (cart) => getItem(obj).chain(getPrice)
|
||||||
|
|
||||||
getNestedPrice({}); // None()
|
getNestedPrice({}) // None()
|
||||||
getNestedPrice({item: {foo: 1}}); // None()
|
getNestedPrice({item: {foo: 1}}) // None()
|
||||||
getNestedPrice({item: {price: 9.99}}); // Some(9.99)
|
getNestedPrice({item: {price: 9.99}}) // Some(9.99)
|
||||||
```
|
```
|
||||||
|
|
||||||
`Option` is also known as `Maybe`. `Some` is sometimes called `Just`. `None` is sometimes called `Nothing`.
|
`Option` is also known as `Maybe`. `Some` is sometimes called `Just`. `None` is sometimes called `Nothing`.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue