├── .gitignore ├── .idea ├── .gitignore ├── fp-ts-talk-code.iml ├── modules.xml └── vcs.xml ├── README.md ├── package-lock.json ├── package.json ├── src ├── Option │ ├── absurd.ts │ ├── compose-with-option.ts │ ├── compose-with-undefined.ts │ ├── option-chain.ts │ ├── option-methods.ts │ ├── option.ts │ └── unions.ts ├── compose-no-pipe.ts ├── compose-pipe-debug.ts ├── compose-pipe.ts ├── create-user-function.ts ├── curry-functions.ts ├── data.ts ├── debug.ts ├── functions.ts ├── readonly.ts ├── string-utils.ts └── types.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/fp-ts-talk-code.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fp-ts-talk-code 2 | Примеры кода для доклада по fp-ts 3 | 4 | # Доклад 5 | [Функциональное программирование с использованием библиотеки fp-ts](https://youtu.be/LoeV7yeItkk "Функциональное программирование с использованием библиотеки fp-ts") 6 | 7 | # Документация 8 | https://gcanti.github.io/fp-ts/ 9 | 10 | # Список литературы 11 | ## Книги 12 | - [Professor Frisby’s Mostly Adequate Guide to Functional Programming](https://github.com/MostlyAdequate/mostly-adequate-guide-ru/blob/master/SUMMARY-ru.md "Professor Frisby’s Mostly Adequate Guide to Functional Programming") 13 | ## Спецификация 14 | - [Fantasy Land Specification](https://github.com/fantasyland/fantasy-land "Fantasy Land Specification") 15 | - [Fantas, Eel, and Specification](http://www.tomharding.me/fantasy-land/ "Fantas, Eel, and Specification") 16 | ## Туториалы 17 | ### Функциональное программирование на TypeScript 18 | - [Функциональное программирование на TypeScript: полиморфизм родов высших порядков](https://habr.com/ru/post/526024/ "Функциональное программирование на TypeScript: полиморфизм родов высших порядков") 19 | - [Функциональное программирование на TypeScript: паттерн «класс типов»](https://habr.com/ru/post/534998/ "Функциональное программирование на TypeScript: паттерн «класс типов»") 20 | - [Функциональное программирование на TypeScript: Option и Either](https://habr.com/ru/post/544636/ "Функциональное программирование на TypeScript: Option и Either") 21 | - [Функциональное программирование на TypeScript: задачи (tasks) как альтернатива промисам](https://habr.com/ru/post/548622/ "Функциональное программирование на TypeScript: задачи (tasks) как альтернатива промисам") 22 | ### Getting started with fp-ts 23 | - [Introduction to Functional Programming](https://github.com/enricopolanski/functional-programming "Introduction to Functional Programming") 24 | - [Getting started with fp-ts: Eq](https://dev.to/gcanti/getting-started-with-fp-ts-setoid-39f3) 25 | - [Getting started with fp-ts: Ord](https://dev.to/gcanti/getting-started-with-fp-ts-ord-5f1e "Getting started with fp-ts: Ord") 26 | - [Getting started with fp-ts: Semigroup](https://dev.to/gcanti/getting-started-with-fp-ts-semigroup-2mf7 "Getting started with fp-ts: Semigroup") 27 | - [Getting started with fp-ts: Monoid](https://dev.to/gcanti/getting-started-with-fp-ts-monoid-ja0 "Getting started with fp-ts: Monoid") 28 | - [Getting started with fp-ts: Category](https://dev.to/gcanti/getting-started-with-fp-ts-category-4c9a "Getting started with fp-ts: Category") 29 | - [Getting started with fp-ts: Functor](https://dev.to/gcanti/getting-started-with-fp-ts-functor-36ek "Getting started with fp-ts: Functor") 30 | - [Getting started with fp-ts: Applicative](https://dev.to/gcanti/getting-started-with-fp-ts-applicative-1kb3 "Getting started with fp-ts: Applicative") 31 | - [Getting started with fp-ts: Monad](https://dev.to/gcanti/getting-started-with-fp-ts-monad-6k "Getting started with fp-ts: Monad") 32 | ### Practical Guide to Fp-ts 33 | - [Practical Guide to Fp-ts P1: Pipe and Flow](https://rlee.dev/practical-guide-to-fp-ts-part-1 "Practical Guide to Fp-ts P1: Pipe and Flow") 34 | - [Practical Guide to Fp-ts P2: Option, Map, Flatten, Chain](https://rlee.dev/practical-guide-to-fp-ts-part-2 "Practical Guide to Fp-ts P2: Option, Map, Flatten, Chain") 35 | - [Practical Guide to Fp-ts P3: Task, Either, TaskEither](https://rlee.dev/practical-guide-to-fp-ts-part-3 "Practical Guide to Fp-ts P3: Task, Either, TaskEither") 36 | - [Practical Guide to Fp-ts P4: Arrays, Semigroups, Monoids](https://rlee.dev/practical-guide-to-fp-ts-part-4 "Practical Guide to Fp-ts P4: Arrays, Semigroups, Monoids") 37 | - [Practical Guide to Fp-ts P5: Apply, Sequences, and Traversals](https://rlee.dev/practical-guide-to-fp-ts-part-5 "Practical Guide to Fp-ts P5: Apply, Sequences, and Traversals") 38 | - [Practical Guide to Fp-ts P6: The Do Notation](https://rlee.dev/practical-guide-to-fp-ts-p6-the-do-notation "Practical Guide to Fp-ts P6: The Do Notation") 39 | ## TypeScript 40 | - [TypeScript: Раскладываем tsconfig по полочкам. Часть 1](https://habr.com/ru/post/542234/ "TypeScript: Раскладываем tsconfig по полочкам. Часть 1") 41 | - [TypeScript: Раскладываем tsconfig по полочкам. Часть 2 — Всё про строгость](https://habr.com/ru/post/557738/ "TypeScript: Раскладываем tsconfig по полочкам. Часть 2 — Всё про строгость") 42 | - [Discriminated Unions](https://basarat.gitbook.io/typescript/type-system/discriminated-unions "Discriminated Unions") 43 | ## ESLint 44 | - [eslint-plugin-functional](https://github.com/jonaskello/eslint-plugin-functional "eslint-plugin-functional") 45 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fp-ts-talk-code", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@cspotcode/source-map-consumer": { 8 | "version": "0.8.0", 9 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", 10 | "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==", 11 | "dev": true 12 | }, 13 | "@cspotcode/source-map-support": { 14 | "version": "0.7.0", 15 | "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.7.0.tgz", 16 | "integrity": "sha512-X4xqRHqN8ACt2aHVe51OxeA2HjbcL4MqFqXkrmQszJ1NOUuUu5u6Vqx/0lZSVNku7velL5FC/s5uEAj1lsBMhA==", 17 | "dev": true, 18 | "requires": { 19 | "@cspotcode/source-map-consumer": "0.8.0" 20 | } 21 | }, 22 | "@tsconfig/node10": { 23 | "version": "1.0.8", 24 | "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", 25 | "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==", 26 | "dev": true 27 | }, 28 | "@tsconfig/node12": { 29 | "version": "1.0.9", 30 | "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", 31 | "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==", 32 | "dev": true 33 | }, 34 | "@tsconfig/node14": { 35 | "version": "1.0.1", 36 | "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", 37 | "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==", 38 | "dev": true 39 | }, 40 | "@tsconfig/node16": { 41 | "version": "1.0.2", 42 | "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", 43 | "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==", 44 | "dev": true 45 | }, 46 | "@types/uuid": { 47 | "version": "8.3.3", 48 | "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.3.tgz", 49 | "integrity": "sha512-0LbEEx1zxrYB3pgpd1M5lEhLcXjKJnYghvhTRgaBeUivLHMDM1TzF3IJ6hXU2+8uA4Xz+5BA63mtZo5DjVT8iA==", 50 | "dev": true 51 | }, 52 | "acorn": { 53 | "version": "8.6.0", 54 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", 55 | "integrity": "sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==", 56 | "dev": true 57 | }, 58 | "acorn-walk": { 59 | "version": "8.2.0", 60 | "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", 61 | "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", 62 | "dev": true 63 | }, 64 | "arg": { 65 | "version": "4.1.3", 66 | "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", 67 | "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", 68 | "dev": true 69 | }, 70 | "create-require": { 71 | "version": "1.1.1", 72 | "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", 73 | "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", 74 | "dev": true 75 | }, 76 | "diff": { 77 | "version": "4.0.2", 78 | "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", 79 | "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", 80 | "dev": true 81 | }, 82 | "fp-ts": { 83 | "version": "2.11.5", 84 | "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-2.11.5.tgz", 85 | "integrity": "sha512-OqlwJq1BdpB83BZXTqI+dNcA6uYk6qk4u9Cgnt64Y+XS7dwdbp/mobx8S2KXf2AXH+scNmA/UVK3SEFHR3vHZA==" 86 | }, 87 | "make-error": { 88 | "version": "1.3.6", 89 | "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", 90 | "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", 91 | "dev": true 92 | }, 93 | "prettier": { 94 | "version": "2.5.0", 95 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.0.tgz", 96 | "integrity": "sha512-FM/zAKgWTxj40rH03VxzIPdXmj39SwSjwG0heUcNFwI+EMZJnY93yAiKXM3dObIKAM5TA88werc8T/EwhB45eg==", 97 | "dev": true 98 | }, 99 | "ts-node": { 100 | "version": "10.4.0", 101 | "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.4.0.tgz", 102 | "integrity": "sha512-g0FlPvvCXSIO1JDF6S232P5jPYqBkRL9qly81ZgAOSU7rwI0stphCgd2kLiCrU9DjQCrJMWEqcNSjQL02s6d8A==", 103 | "dev": true, 104 | "requires": { 105 | "@cspotcode/source-map-support": "0.7.0", 106 | "@tsconfig/node10": "^1.0.7", 107 | "@tsconfig/node12": "^1.0.7", 108 | "@tsconfig/node14": "^1.0.0", 109 | "@tsconfig/node16": "^1.0.2", 110 | "acorn": "^8.4.1", 111 | "acorn-walk": "^8.1.1", 112 | "arg": "^4.1.0", 113 | "create-require": "^1.1.0", 114 | "diff": "^4.0.1", 115 | "make-error": "^1.1.1", 116 | "yn": "3.1.1" 117 | } 118 | }, 119 | "typescript": { 120 | "version": "4.5.2", 121 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", 122 | "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==" 123 | }, 124 | "uuid": { 125 | "version": "8.3.2", 126 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", 127 | "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" 128 | }, 129 | "yn": { 130 | "version": "3.1.1", 131 | "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", 132 | "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", 133 | "dev": true 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fp-ts-talk-code", 3 | "version": "1.0.0", 4 | "description": "Примеры кода для доклада по fp-ts", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/dkhovrich/fp-ts-talk-code.git" 12 | }, 13 | "keywords": [], 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/dkhovrich/fp-ts-talk-code/issues" 18 | }, 19 | "homepage": "https://github.com/dkhovrich/fp-ts-talk-code#readme", 20 | "dependencies": { 21 | "fp-ts": "^2.11.5", 22 | "typescript": "^4.5.2", 23 | "uuid": "^8.3.2" 24 | }, 25 | "devDependencies": { 26 | "@types/uuid": "^8.3.3", 27 | "prettier": "^2.5.0", 28 | "ts-node": "^10.4.0" 29 | }, 30 | "prettier": { 31 | "trailingComma": "none", 32 | "tabWidth": 4, 33 | "semi": true, 34 | "singleQuote": false 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Option/absurd.ts: -------------------------------------------------------------------------------- 1 | function absurd(_: never): A { 2 | throw new Error('Called `absurd` function which should be uncallable') 3 | } -------------------------------------------------------------------------------- /src/Option/compose-with-option.ts: -------------------------------------------------------------------------------- 1 | import {createUsers} from "../data"; 2 | import {constant, pipe} from "fp-ts/function"; 3 | import * as O from "fp-ts/Option"; 4 | import * as A from "fp-ts/ReadonlyArray"; 5 | import {byEmail, createFullName} from "../types"; 6 | import {stringHash} from "../string-utils"; 7 | 8 | const users = createUsers(); 9 | 10 | const optionalUser = pipe( 11 | users, 12 | A.findFirst(byEmail("spider-man@gmail.com")) 13 | ); 14 | 15 | console.log(optionalUser); // Option -> { _tag: 'Some', value: { id: 1, email: "spiderman@gmail.com" } } 16 | 17 | const optionalFullName = pipe(optionalUser, O.map(createFullName)); 18 | 19 | console.log(optionalFullName); // Option -> { _tag: 'Some', value: 'Peter Parker' } 20 | 21 | const hero = pipe( 22 | users, 23 | A.findFirst(byEmail("spider-man@gmail.com")), 24 | O.map(createFullName) 25 | ); 26 | 27 | console.log(hero); // Option -> { _tag: 'Some', value: 'Peter Parker' } 28 | 29 | const girl = pipe( 30 | users, 31 | A.findFirst(byEmail("marry-watson@gmail.com")), 32 | O.map(createFullName) 33 | ); 34 | 35 | console.log(girl); // Option -> { _tag: 'None' } 36 | 37 | const result = pipe( 38 | users, 39 | A.findFirst(byEmail("spider-man@gmail.com")), 40 | O.map(createFullName), 41 | O.toUndefined 42 | ); 43 | 44 | console.log(result); // string -> Peter Parker 45 | 46 | const result1 = pipe( 47 | users, 48 | A.findFirst(byEmail("iron-man@gmail.com")), 49 | O.map(createFullName), 50 | O.getOrElse(constant("Proof That Tony Stark Has A Heart")) 51 | ) 52 | 53 | console.log(result1); // Proof That Tony Stark Has A Heart 54 | 55 | const fullNameHash = pipe( 56 | users, 57 | A.findFirst(byEmail("spiderman@gmail.com")), 58 | O.map(createFullName), 59 | O.map(stringHash) 60 | ); 61 | 62 | console.log(fullNameHash); // Option -> { _tag: 'Some', value: -1021137173 } -------------------------------------------------------------------------------- /src/Option/compose-with-undefined.ts: -------------------------------------------------------------------------------- 1 | import {createUsers} from "../data"; 2 | import {pipe} from "fp-ts/function"; 3 | import {byEmail, User} from "../types"; 4 | 5 | const users = createUsers(); 6 | 7 | declare function createFullName(user: User): string; 8 | 9 | // TS2345: Argument of type 'User | undefined' is not assignable to parameter of type 'User' 10 | // Type 'undefined' is not assignable to type 'User'. 11 | const result = pipe( 12 | users.find(byEmail("spider-man@gmail.com")), 13 | createFullName 14 | ); -------------------------------------------------------------------------------- /src/Option/option-chain.ts: -------------------------------------------------------------------------------- 1 | import { Comment } from "../types"; 2 | import {createComments, createUsers} from "../data"; 3 | import {constant, pipe} from "fp-ts/function"; 4 | import * as O from "fp-ts/Option"; 5 | import * as A from "fp-ts/ReadonlyArray"; 6 | import * as R from "fp-ts/Record"; 7 | import {byEmail} from "../types"; 8 | 9 | const users = createUsers(); 10 | 11 | const comments = createComments(); 12 | 13 | // Option> 14 | const a = pipe( 15 | users, 16 | A.findFirst(byEmail("spider-man@gmail.com")), 17 | O.map(user => R.lookup(user.id, comments)) 18 | ) 19 | 20 | console.log(a); // { _tag: 'Some', value: { _tag: 'Some', value: [ [Object] ] } } 21 | 22 | // Option 23 | const b = pipe( 24 | users, 25 | A.findFirst(byEmail("spider-man@gmail.com")), 26 | O.chain(user => R.lookup(user.id, comments)) 27 | ); 28 | 29 | console.log(b); // { _tag: 'Some', value: [ [Object] ] } 30 | 31 | // readonly Comment[] 32 | const c = pipe( 33 | users, 34 | A.findFirst(byEmail("spider-man@gmail.com")), 35 | O.chain(user => R.lookup(user.id, comments)), 36 | O.getOrElse(constant([])) 37 | ) 38 | 39 | console.log(c); // [{ userId: '1', text: 'With great power comes great responsibility' }] -------------------------------------------------------------------------------- /src/Option/option-methods.ts: -------------------------------------------------------------------------------- 1 | import {Option} from "fp-ts/Option"; 2 | 3 | interface Predicate { 4 | (a: A): boolean 5 | } 6 | 7 | // Methods 8 | declare function lookup(i: number): (as: ReadonlyArray) => Option 9 | 10 | declare function lookup(i: number, as: ReadonlyArray): Option 11 | 12 | declare function findFirst(predicate: Predicate): (as: ReadonlyArray) => Option 13 | 14 | declare const head: (as: readonly A[]) => Option 15 | 16 | declare const last: (as: readonly A[]) => Option 17 | 18 | declare const updateAt: (i: number, a: A) => (as: readonly A[]) => Option -------------------------------------------------------------------------------- /src/Option/option.ts: -------------------------------------------------------------------------------- 1 | import {absurd} from "fp-ts/function"; 2 | 3 | export type None = { 4 | readonly _tag: 'None'; 5 | }; 6 | 7 | export type Some = { 8 | readonly _tag: 'Some'; 9 | readonly value: A; 10 | }; 11 | 12 | export type Option = None | Some; 13 | 14 | type OptionMap = (f: (a: A) => B) => (fa: Option) => Option; 15 | 16 | const map: OptionMap = f => option => { 17 | switch (option._tag) { 18 | case "None": 19 | return option; 20 | case "Some": 21 | return {_tag: "Some", value: f(option.value)}; 22 | default: 23 | return absurd(option) 24 | } 25 | } 26 | 27 | type OptionChain = (f: (a: A) => Option) => (fa: Option) => Option; 28 | 29 | const chain: OptionChain = f => option => { 30 | switch (option._tag) { 31 | case "None": 32 | return option; 33 | case "Some": 34 | return f(option.value); 35 | default: 36 | return absurd(option) 37 | } 38 | } -------------------------------------------------------------------------------- /src/Option/unions.ts: -------------------------------------------------------------------------------- 1 | import {User} from "../types"; 2 | 3 | type Success = { readonly type: "success"; readonly value: T; } 4 | 5 | type Fail = { readonly type: "fail"; readonly error: Error; } 6 | 7 | type Result = Success | Fail; 8 | 9 | declare function loadUsers(): Result; 10 | 11 | const result = loadUsers(); 12 | 13 | console.log(result.value); // Property 'value' does not exist on type 'Result' 14 | console.log(result.error); // Property 'error' does not exist on type 'Result' 15 | 16 | switch (result.type) { 17 | case "success": 18 | console.log(result.value); 19 | break; 20 | case "fail": 21 | console.log(result.error); 22 | break; 23 | default: 24 | absurd(result); 25 | } 26 | -------------------------------------------------------------------------------- /src/compose-no-pipe.ts: -------------------------------------------------------------------------------- 1 | import {multiply, sum} from "./functions"; 2 | import {valueToString} from "./string-utils"; 3 | 4 | const value = 2; 5 | const valueAfterSum = sum(value, 2); 6 | const valueAfterMultiply = multiply(valueAfterSum, 3); 7 | const valueString = valueToString(valueAfterMultiply); 8 | 9 | console.log(valueString); // Value: 12 10 | 11 | const result = valueToString(multiply(sum(value, 2), 3)); 12 | console.log(result); // Value: 12 13 | 14 | export {}; -------------------------------------------------------------------------------- /src/compose-pipe-debug.ts: -------------------------------------------------------------------------------- 1 | import {pipe} from "fp-ts/function"; 2 | import {multiply, sum} from "./curry-functions"; 3 | import {valueToString} from "./string-utils"; 4 | import {debug} from "./debug"; 5 | 6 | const value = 2; 7 | const result = pipe( 8 | value, 9 | sum(2), 10 | debug("after sum"), // DEBUG:after sum : 4 11 | multiply(3), 12 | debug("after multiply"), // DEBUG:after multiply : 12 13 | valueToString 14 | ); 15 | 16 | console.log(result); // Value: 12 -------------------------------------------------------------------------------- /src/compose-pipe.ts: -------------------------------------------------------------------------------- 1 | import {pipe} from "fp-ts/function"; 2 | import {multiply, sum} from "./curry-functions"; 3 | import {valueToString} from "./string-utils"; 4 | 5 | const value = 2; 6 | const result = pipe(value, sum(2), multiply(3), valueToString); 7 | 8 | console.log(result); // Value: 12 -------------------------------------------------------------------------------- /src/create-user-function.ts: -------------------------------------------------------------------------------- 1 | import { v4 as uuid } from 'uuid'; 2 | 3 | type User = { 4 | readonly id: string; 5 | readonly email: string; 6 | }; 7 | 8 | // declare function createUser(email: string): User; 9 | 10 | function createUser(email: string): User { 11 | if (email.length > 256) { 12 | throw new Error("Email is too long!"); 13 | } 14 | 15 | if (!email.includes("@")) { 16 | throw new Error("Email is invalid!") 17 | } 18 | 19 | return { id: uuid(), email}; 20 | } -------------------------------------------------------------------------------- /src/curry-functions.ts: -------------------------------------------------------------------------------- 1 | import {byEmail} from "./types"; 2 | import {createUsers} from "./data"; 3 | 4 | export const sum = (a: number) => (b: number): number => a + b; 5 | 6 | export const multiply = (a: number) => (b: number): number => a * b; 7 | 8 | // console.log(sum(2)(2)); 9 | 10 | const users = createUsers(); 11 | 12 | const user = users.find(byEmail("peter-parker@gmail.com")); 13 | 14 | // console.log(user); 15 | 16 | export {}; -------------------------------------------------------------------------------- /src/data.ts: -------------------------------------------------------------------------------- 1 | import {User, Comment, CommentId} from "./types"; 2 | 3 | export const createUsers = (): readonly User[] => [ 4 | {id: "1", email: "spider-man@gmail.com", firstName: "Peter", lastName: "Parker"}, 5 | {id: "2", email: "goblin@gmail.com", firstName: "Norman", lastName: "Osborn"}, 6 | {id: "3", email: "octopus@gmail.com", firstName: "Otto", lastName: "Octavius"}, 7 | ]; 8 | 9 | export const createComments = (): Record => ({ 10 | [1]: [{ userId: "1", text: "With great power comes great responsibility"}] 11 | }); -------------------------------------------------------------------------------- /src/debug.ts: -------------------------------------------------------------------------------- 1 | export const debug = (key: string) => (value: T): T => { 2 | console.log(`DEBUG:${key} : ${value}`); 3 | return value; 4 | } -------------------------------------------------------------------------------- /src/functions.ts: -------------------------------------------------------------------------------- 1 | // Pure 2 | export const sum = (a: number, b: number): number => a + b; 3 | 4 | export const multiply = (a: number, b: number): number => a * b; 5 | 6 | export const subtract = (a: number, b: number): number => a - b; 7 | 8 | export const divide = (a: number, b: number): number => a / b; 9 | 10 | const dateToString = (date: Date): string => date.toDateString(); 11 | 12 | // Impure 13 | const getCurrentDateIso = (): string => new Date().toISOString(); 14 | 15 | const log = (key: string, value: T): void => { 16 | console.log(`${key}: ${value}`); 17 | }; 18 | 19 | const saveToStorage = (key: string, value: T): void => { 20 | localStorage.setItem(key, JSON.stringify(value)); 21 | }; 22 | 23 | const getFromStorage = (key: string): string | null => { 24 | return localStorage.getItem(key); 25 | }; 26 | 27 | const renderString = (selector: string, value: string): void => { 28 | const element = document.querySelector(selector); 29 | if (element !== null) { 30 | element.innerHTML = value; 31 | } 32 | }; -------------------------------------------------------------------------------- /src/readonly.ts: -------------------------------------------------------------------------------- 1 | import {ReadonlyRecord} from "fp-ts/ReadonlyRecord"; 2 | 3 | const arr: readonly number[] = [1, 2, 3]; 4 | 5 | arr.push(4); // TS2551: Property 'push' does not exist on type 'readonly number[]' 6 | arr.pop(); // TS2551: Property 'pop' does not exist on type 'readonly number[]'. 7 | arr.unshift(); // TS2551: Property 'unshift' does not exist on type 'readonly number[]'. 8 | arr.shift(); // TS2551: Property 'shift' does not exist on type 'readonly number[]' 9 | arr.splice(1); // TS2551: Property 'splice' does not exist on type 'readonly number[]' -------------------------------------------------------------------------------- /src/string-utils.ts: -------------------------------------------------------------------------------- 1 | export const valueToString = (value: number): string => `Value: ${value}`; 2 | 3 | export function stringHash(value: string): number { 4 | let hash = 0; 5 | let i: number; 6 | let chr: number; 7 | for (i = 0; i < value.length; i += 1) { 8 | chr = value.charCodeAt(i); 9 | hash = (hash << 5) - hash + chr; 10 | hash |= 0; // Convert to 32bit integer 11 | } 12 | return hash; 13 | } -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | export type UserId = string; 2 | 3 | export type User = { 4 | readonly id: UserId; 5 | readonly email: string; 6 | readonly firstName: string; 7 | readonly lastName: string; 8 | } 9 | 10 | export type CommentId = number; 11 | 12 | export type Comment = { 13 | readonly userId: UserId; 14 | readonly text: string; 15 | } 16 | 17 | export const createFullName = (user: User): string => `${user.firstName} ${user.lastName}`; 18 | 19 | export const byEmail = (email: string) => (user: User): boolean => user.email === email; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Enable incremental compilation */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "ESNext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */ 22 | // "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | 26 | /* Modules */ 27 | "module": "commonjs", /* Specify what module code is generated. */ 28 | // "rootDir": "./", /* Specify the root folder within your source files. */ 29 | // "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */ 30 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 31 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 32 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 33 | // "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */ 34 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 35 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 36 | // "resolveJsonModule": true, /* Enable importing .json files */ 37 | // "noResolve": true, /* Disallow `import`s, `require`s or ``s from expanding the number of files TypeScript should add to a project. */ 38 | 39 | /* JavaScript Support */ 40 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */ 41 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 42 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */ 43 | 44 | /* Emit */ 45 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 46 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 47 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 48 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 49 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */ 50 | // "outDir": "./", /* Specify an output folder for all emitted files. */ 51 | // "removeComments": true, /* Disable emitting comments. */ 52 | "noEmit": true, /* Disable emitting files from a compilation. */ 53 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 54 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */ 55 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 56 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 57 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 58 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 59 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 60 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 61 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 62 | // "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */ 63 | // "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */ 64 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 65 | // "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */ 66 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 67 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 68 | 69 | /* Interop Constraints */ 70 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 71 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 72 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */ 73 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 74 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 75 | 76 | /* Type Checking */ 77 | "strict": true, /* Enable all strict type-checking options. */ 78 | "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */ 79 | "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */ 80 | "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 81 | "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */ 82 | "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 83 | "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */ 84 | "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ 85 | "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 86 | "noUnusedLocals": false, /* Enable error reporting when a local variables aren't read. */ 87 | "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */ 88 | "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 89 | "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 90 | "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 91 | "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ 92 | "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 93 | "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */ 94 | "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 95 | "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 96 | 97 | /* Completeness */ 98 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 99 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 100 | }, 101 | "include": ["src/**/*.ts"] 102 | } 103 | --------------------------------------------------------------------------------