Skip to content

Commit f299446

Browse files
authored
Merge pull request hemanth#186 from PinkaminaDianePie/patch-1
Added info about function totality
2 parents b28cba2 + 9c3077f commit f299446

File tree

1 file changed

+78
-0
lines changed

1 file changed

+78
-0
lines changed

Diff for: readme.md

+78
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ __Table of Contents__
6060
* [Sum type](#sum-type)
6161
* [Product type](#product-type)
6262
* [Option](#option)
63+
* [Totality](#totality)
6364
* [Functional Programming Libraries in JavaScript](#functional-programming-libraries-in-javascript)
6465

6566

@@ -907,6 +908,83 @@ getNestedPrice({item: {price: 9.99}}) // Some(9.99)
907908

908909
`Option` is also known as `Maybe`. `Some` is sometimes called `Just`. `None` is sometimes called `Nothing`.
909910

911+
## Totality
912+
### Total functions
913+
The total function is just like a function in math - it will always return a result of the expected type for expected inputs and will always terminate. The easiest example is `identity` function:
914+
```js
915+
// identity :: a -> a
916+
const identity = a => a
917+
```
918+
or `negate`:
919+
```js
920+
// negate :: Boolean -> Boolean
921+
const negate = value => !value
922+
```
923+
Such functions always meet the requirements, you just can't provide such arguments, which satisfy inputs but break outputs.
924+
### Partial functions
925+
It's a function which violates the requirements to be total - it may return an unexpected result with some inputs or it may never terminate. Partial functions add cognitive overhead, they are harder to reason about and they can lead to runtime errors. Some examples:
926+
```js
927+
// example 1: sum of the list
928+
// sum :: [Number] -> Number
929+
const sum = arr => arr.reduce((a, b) => a + b)
930+
sum([1, 2, 3]) // 6
931+
sqrt([]) // TypeError: Reduce of empty array with no initial value
932+
933+
// example 2: get the first item in list
934+
// first :: [A] -> A
935+
const first = a => a[0]
936+
first([42]) // 42
937+
first([]) // undefined
938+
//or even worse:
939+
first([[42]])[0] // 42
940+
first([])[0] // Uncaught TypeError: Cannot read property '0' of undefined
941+
942+
// example 3: repeat function N times
943+
// times :: Number -> (Number -> Number) -> Number
944+
const times = n => fn => n && (fn(n), times(n - 1)(fn))
945+
times(3)(console.log)
946+
// 3
947+
// 2
948+
// 1
949+
times(-1)(console.log)
950+
// RangeError: Maximum call stack size exceeded
951+
```
952+
### Avoiding partial functions
953+
Partial functions are dangerous, you can sometimes get the expected result, sometimes the wrong result, and sometimes your function can't stop the calculations at all. The input of partial functions should be always checked, and it can be hard to track all edge cases through entire applications, the easiest way to deal with it it's just to convert all partial functions to the total. General advice can be the usage of `Optional` type, providing default values for edge cases and checking function conditions to make them always terminate:
954+
```js
955+
// example 1: sum of the list
956+
// we can provide default value so it will always return result
957+
// sum :: [Number] -> Number
958+
const sum = arr => arr.reduce((a, b) => a + b, 0)
959+
sum([1, 2, 3]) // 6
960+
sqrt([]) // 0
961+
962+
// example 2: get the first item in list
963+
// change result to Option
964+
// first :: [A] -> Option A
965+
const first = a => a.length ? Some(a[0]) : None()
966+
first([[42]]).map(a => console.log(a)) // 42
967+
first([]).map(a => console.log(a)) // console.log won't execute at all
968+
//our previous worst case
969+
first([[42]]).map(a => console.log(a[0])) // 42
970+
first([]).map(a => console.log(a[0])) // won't execte, so we won't have error here
971+
// more of that, you will know by function return type (Option)
972+
// that you should use `.map` method to access the data and you will never forget
973+
// to check your input because such check become built-in into the function
974+
975+
// example 3: repeat function N times
976+
// we should make function always terminate by changing conditions:
977+
// times :: Number -> (Number -> Number) -> Number
978+
const times = n => fn => n > 0 && (fn(n), times(n - 1)(fn))
979+
times(3)(console.log)
980+
// 3
981+
// 2
982+
// 1
983+
times(-1)(console.log)
984+
// won't execute anything
985+
```
986+
If you will change all your functions from partial to total, it can prevent you from having runtime exceptions, will make code easier to reason about and easier to maintain.
987+
910988
## Functional Programming Libraries in JavaScript
911989

912990
* [mori](https://github.com/swannodette/mori)

0 commit comments

Comments
 (0)