SlideShare a Scribd company logo
Функциональное программирование
с использованием библиотеки fp-ts
Обо мне
● Программирую с 2014 года
● Активно применяю функциональный
подход на текущем проекте
Дмитрий Ховрич
Software Engineer в
Что такое функциональное
программирование?
Функциональное программирование
Функциональное программирование - это
программирование с использованием чистых
математических функций
Зачем писать код в функциональном стиле?
Функциональное программирование, как и любая другая парадигма,
накладывает дополнительные ограничения и правила.
Соблюдение этих ограничений и правил позволяет писать более надежный
код, который легче тестировать и повторно использовать.
Как мы можем себя ограничить во время написания кода?
“Хард” ограничения
● Компилятор TypeScript
○ Максимально строгий режим
○ TypeScript: Раскладываем tsconfig по
полочкам. Часть 1
○ TypeScript: Раскладываем tsconfig по
полочкам. Часть 2 — Всё про строгость
● ESLint
“Софт” ограничения
● Парадигма программирования
● Паттерны и лучшие практики
● Код ревью
Библиотека fp-ts
Библиотека fp-ts позволяет писать чистые функциональные приложения,
построенные поверх высокоуровневых абстракций из таких языков, как
Haskell, PureScript, и Scala.
● Паттерны и надежные абстракции из функционального мира
● Утилиты для композиции функций
● Функциональные типы данных
● Классы типов (type classes)
Класс типов
Класс типов определяет множество типизированных функций и констант,
которые должны существовать для каждого типа, который принадлежит
данному классу.
Основаны на теории категорий
Теория категорий
Теория категорий — раздел математики, изучающий свойства отношений
между математическими объектами, не зависящие от внутренней структуры
объектов.
Теперь еще математические теории учить?
Теория категорий
Вам не нужно изучать теорию
категорий для того, чтобы
использовать
функциональный подход в
Ваших программах!
Класс типов Show
interface Show<A> {
readonly show: (a: A) => string;
}
Тип A принадлежит классу Show, если для A определена функция show : (a:
A) => string
Реализации класса типов Show
const showNumber: Show<number> = {
show: n => n.toString()
};
const showUser: Show<User> = {
show: user => `User "${user.name}", ${user.age} years old`
};
Из чего состоит функциональное
программирование?
Из чего состоит функциональное программирование?
Концепции, которые
“лежат на поверхности”
Концепции, которые
“спрятаны поглубже”
Концепции, которые “лежат на поверхности”
● Иммутабельные данные
● Чистые функции (referentially transparent)
○ Каррирование
● Побочные эффекты
● “Честные” сигнатуры функций (method signature honesty)
● Композиция функций (бесточечный стиль)
Иммутабельные данные
● Изменение данных путем пересоздания объектов / массивов
● TypeScript не поддерживает иммутабельные типы данных в runtime
○ Но скоро могут появятся записи (records) и кортежи (tuples)
● Ключевое слово readonly
○ ReadonlyArray
○ ReadonlyRecord
○ ReadonlyMap
○ ReadonlySet
● eslint-plugin-functional
Иммутабельные данные
const arr: readonly number[] = [1, 2, 3];
arr.push(4); // Property 'push' does not exist on type 'readonly number[]'
arr.pop(); // Property 'pop' does not exist on type 'readonly number[]'.
arr.unshift(); // Property 'unshift' does not exist on type 'readonly number[]'.
arr.shift(); // Property 'shift' does not exist on type 'readonly number[]'
arr.splice(1); // Property 'splice' does not exist on type 'readonly number[]'
Чистые функции
const sum = (a: number, b: number): number => a + b;
const multiply = (a: number, b: number): number => a * b;
const subtract = (a: number, b: number): number => a - b;
const divide = (a: number, b: number): number => a / b;
const dateToString = (date: Date): string => date.toDateString();
Чистые каррированые функции
const sum = (a: number) => (b: number): number => a + b;
const multiply = (a: number) => (b: number): number => a * b;
const byEmail = (email: string) => (user: User): boolean =>
user.email === email;
// Удобно применять когда функция ожидает предикат на вход
const user = users.find(byEmail("peter-parker@gmail.com"));
Чистые функции могут мутировать данные внутри себя
function shuffle<T>(array: readonly T[]): readonly T[] {
const arrayCopy = [...array];
for (let i = arrayCopy.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
const temp = arrayCopy[i];
arrayCopy[i] = array[j];
arrayCopy[j] = temp;
}
return arrayCopy;
}
Побочные эффекты
Побочный эффект - это изменение состояния системы или наблюдаемое
взаимодействие с окружающим миром, происходящее во время вычисления
результата.
● изменения в файловой системе
● обращение к базе данных
● выполнение http-запроса
● выброс ошибок
● вывод на экран / запись в лог
● получение данных от пользователя
● выполнение запроса к DOM
Любое взаимодействие со средой вне функции является побочным эффектом.
Функции с побочными эффектами
const getCurrentDateIso = (): string => new Date().toISOString();
const log = <T>(key: string, value: T): void => console.log(`${key}: ${value}`);
const saveToStorage = <T>(key: string, value: T): void => {
localStorage.setItem(key, JSON.stringify(value));
};
const getFromStorage = (key: string): string | null => localStorage.getItem(key);
“Честные” сигнатуры функций
type User = {
readonly id: string;
readonly email: string;
};
declare function createUser(email: string): User;
Скрытые побочные эффекты внутри функции
function createUser(email: string): User {
if (email.length > 256) {
throw new Error("Email is too long!");
}
if (!email.includes("@")) {
throw new Error("Email is invalid!");
}
return { id: uuid(), email };
}
Композиция вызовов функций
Последовательные вызовы без композиции
const value = 2;
const valueAfterSum = sum(value, 2);
const valueAfterMultiply = multiply(valueAfterSum, 3);
const result = valueToString(valueAfterMultiply);
console.log(valueString); // 12
Неудобная композиция функций
const result =
valueToString(multiply(sum(value, 2), 3));
console.log(result); // 12
Композиция функций с применением pipe
import {pipe} from "fp-ts/function";
const value = 2;
const result = pipe(value, sum(2), multiply(3),
valueToString);
console.log(result); // 12
Как дебажить? Логами
const debug = <T>(key: string) => (value: T): T => {
console.log(`DEBUG:${key} : ${value}`);
return value;
}
Как дебажить? Логами
const result = pipe(
value,
sum(2),
debug("sum"), // DEBUG:sum : 4
multiply(3),
debug("multiply"), // DEBUG:multiply : 12
valueToString
);
console.log(result); // 12
Как дебажить? Логами
Используйте ESLint правило
no-restricted-imports для того, чтобы
“отловить” забытые debug функции
на этапе git commit или CI
А что может пойти не так?
declare function getUsers(): readonly User[];
declare function createFullName(user: User): string;
const users = getUsers();
const result = pipe(
users.find(byEmail("spider-man@gmail.com")),
createFullName
);
Проблема композиции функций. Код скомпилируется?
Проблема композиции функций
// Argument of type 'User | undefined' is not
assignable to parameter of type 'User'.
// Type 'undefined' is not assignable to type 'User'.
const result = pipe(
users.find(byEmail("spider-man@gmail.com")),
createFullName
);
Концепции, которые “спрятаны поглубже”
Объединения типов с дискриминантом
Объединения типов с дискриминантом
Объединения типов с дискриминантом - это объединения у которых
присутствует одно общее поле (с уникальным символом, чаще всего
строковым литералом).
Оно и будет служить дискриминантом, чтобы TypeScript по нему вывел что
же за тип у вас сейчас в руках.
Объединения типов с дискриминантом
type Success<T> = {
readonly type: "success";
readonly value: T;
}
type Fail = {
readonly type: "fail";
readonly error: Error;
}
type Result<T> = Success<T> | Fail;
Объединения типов с дискриминантом
declare function loadUsers(): Result<readonly User[]>;
const result = loadUsers();
// Property 'value' does not exist on type 'Result<readonly User[]>'
console.log(result.value);
// Property 'error' does not exist on type 'Result<readonly User[]>'
console.log(result.error);
Объединения типов с дискриминантом
switch (result.type) {
case "success":
console.log(result.value);
break;
case "fail":
console.log(result.error);
break;
default:
absurd(result); // typeof result is never
}
Тело функции absurd
function absurd<A>(_: never): A {
throw new Error('Called `absurd` function which
should be uncallable')
}
Option
Option
Option - конструкция, описывающая наличие или отсутствие значения.
С помощью функций map и chain позволяет работать с цепочками вызовов
функций даже если какая-то из них может вернуть null / undefined.
Option - тип данных, предоставляемый библиотекой fp-ts
● Описание типа
● Функции для взаимодействия
Описание типа Option
type None = {
readonly _tag: 'None';
};
type Some<A> = {
readonly _tag: 'Some';
readonly value: A;
};
type Option<A> = None | Some<A>;
Функции массивов, возвращающие Option
ReadonlyArray - head
declare const head:
<A>(as: readonly A[]) => Option<A>
ReadonlyArray - last
declare const last:
<A>(as: readonly A[]) => Option<A>
ReadonlyArray - lookup
declare const lookup:
(i: number) =>
<A>(as: ReadonlyArray<A>) =>
Option<A>
ReadonlyArray - findFirst
declare const findFirst:
<A>(predicate: Predicate<A>) =>
(as: ReadonlyArray<A>) =>
Option<A>
Predicate
interface Predicate<A> {
(a: A): boolean
}
Пример использование Option
import {findFirst} from "fp-ts/ReadonlyArray";
// Option<User>
// { _tag: 'Some', value: { id: 1, email: "spiderman@gmail.com" } }
const value = pipe(
users,
findFirst(byEmail("spider-man@gmail.com"))
);
Пример использование Option
import { map } from "fp-ts/Option";
// Option<string>
// { _tag: 'Some', value: 'Peter Parker' }
const value = pipe(
users,
findFirst(byEmail("spider-man@gmail.com")),
map(createFullName)
);
Пример использование Option
import { map } from "fp-ts/Option";
// Option<string>
// { _tag: 'None' }
const value = pipe(
users,
findFirst(byEmail("mary-jane-watson@gmail.com")),
map(createFullName)
);
Описание типа функции Map для Option
type OptionMap =
<A, B>(f: (a: A) => B) =>
(fa: Option<A>) => Option<B>;
Реализация функции Map для Option
const map: OptionMap = f => option => {
switch (option._tag) {
case "None":
return option;
case "Some":
return { _tag: "Some", value: f(option.value) };
default:
return absurd(option)
}
}
map-ов может быть много
declare function stringHash(value: string): number;
// Option<number>
const value = pipe(
users,
findFirst(byEmail("spider-man@gmail.com")),
map(createFullName),
map(stringHash)
);
Функтор — это класс типов, для которых определена функция map и
соблюдаются некоторые законы:
● Закон идентичности
● Закон композиции
Законы - это ограничения, которые накладываются на реализацию функции
map.
В ежедневной работе Вам не придется писать свои функторы. Все
необходимые функторы реализованы в библиотеке fp-ts.
Функтор
Вложенные Option<T> - плохая практика
import {lookup} from "fp-ts/Record";
const comments: Record<UserId, readonly Comment[]> = {};
// Option<Option<readonly Comment[]>>
const value = pipe(
users,
findFirst(byEmail("spider-man@gmail.com")),
map(user => lookup(user.id, comments))
)
Record - lookup
declare const lookup:
<A>(k: string, r: Record<string, A>):
Option<A>
Как избавиться от вложенных Option<T>
import {chain} from "fp-ts/ReadonlyArray";
const comments: Record<UserId, readonly Comment[]> = {};
// Option<readonly Comment[]>
const value = pipe(
users,
findFirst(byEmail("spider-man@gmail.com")),
chain(user => lookup(user.id, comments))
)
Описание типа функции Chain для Option
type OptionChain =
<A, B>(f: (a: A) => Option<B>) =>
(fa: Option<A>) => Option<B>;
Реализация функции Chain для Option
const chain: OptionChain = f => option => {
switch (option._tag) {
case "None":
return option;
case "Some":
return f(option.value);
default:
return absurd(option)
}
}
Монада
Монада - это класс типов, для которой определены функции map, chain, of и
соблюдаются некоторые законы:
● Закон идентичности
● Закон ассоциативности
Законы - это ограничения, которые накладываются на реализацию функции
chain.
В ежедневной работе Вам не придется писать свои монады. Все
необходимые монады реализованы в библиотеке fp-ts.
Монады используются для последовательных вычислений.
Деструкторы
Как достать значение из Option?
import {toUndefined} from "fp-ts/Option";
// string | undefined
const value = pipe(
users,
findFirst(byEmail("spider-man@gmail.com")),
map(createFullName),
toUndefined
);
Как достать значение из Option?
import {constant} from "fp-ts/function";
import {getOrElse} from "fp-ts/Option";
// string
const value = pipe(
users,
findFirst(byEmail("spider-man@gmail.com")),
map(createFullName),
getOrElse(constant<string>("NO_NAME"))
);
Что еще интересного есть в fp-ts?
● Either - для работы с ошибками
● IO - для работы с побочными эффектами
● Task - для работы с асинхронным кодом
● TaskEither - для работы с асинхронным кодом, который может вернуть ошибку
● Reader - внедрение зависимостей в функциональном стиле
● State - для хранения состояния в композиции функций
● ReaderTaskEither - внедрение зависимостей в асинхронную функцию, которая
может вернуть ошибку
● Eq - для сравнения данных по значению
● Ord - для сортировки данных
● Semigroup - для конкатенации данных
● Monoid - для “сворачивания” данных (reduce)
Что почитать? Код из презентации
https://github.com/dkhovrich/fp-ts-talk-code#readme
Где пообщаться / попросить о помощи?
https://t.me/fp_ts
Вакансия в команду Spark
● продуктовая компания и возможность влиять на сам продукт
● команда профессионалов
● стек технологий (TypeScript, React, RxJS, fp-ts)
● пробуем новые инструменты, подходы
● достойный компенсационный пакет от компании
● гибридный гибкий график (офис, ремоут)
Все вакансии Readdle - https://readdle.com/careers 🚀
Контакты
Почта: khovrych.d@gmail.com
Дмитрий Ховрич
Software Engineer в
Спасибо!

More Related Content

Similar to Функциональное программирование с использованием библиотеки fp-ts | Odessa Frontend Meetup #19

Step cpp022
Step cpp022Step cpp022
Step cpp022
Evgenij Laktionov
 
C# Desktop. Занятие 16.
C# Desktop. Занятие 16.C# Desktop. Занятие 16.
C# Desktop. Занятие 16.
Igor Shkulipa
 
Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU
 
Как приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVMКак приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVM
Tech Talks @NSU
 
Спецкурс "Современные практики разработки ПО", 2013-2014 уч. год, занятие 2
Спецкурс "Современные практики разработки ПО", 2013-2014 уч. год, занятие 2Спецкурс "Современные практики разработки ПО", 2013-2014 уч. год, занятие 2
Спецкурс "Современные практики разработки ПО", 2013-2014 уч. год, занятие 27bits
 
Лекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksЛекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building Blocks
Mikhail Kurnosov
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Sergey Platonov
 
Иван Пузыревский — Введение в асинхронное программирование
Иван Пузыревский — Введение в асинхронное программированиеИван Пузыревский — Введение в асинхронное программирование
Иван Пузыревский — Введение в асинхронное программирование
Yandex
 
Discovering Lambdas in Java 8
Discovering Lambdas in Java 8Discovering Lambdas in Java 8
Discovering Lambdas in Java 8
Stfalcon Meetups
 
C++ Базовый. Занятие 03.
C++ Базовый. Занятие 03.C++ Базовый. Занятие 03.
C++ Базовый. Занятие 03.
Igor Shkulipa
 
вспомогательные алгоритмы
вспомогательные алгоритмывспомогательные алгоритмы
вспомогательные алгоритмыЕлена Ключева
 
Использование шаблонов и RTTI для конфигурации симулятора флеш-накопителя - Г...
Использование шаблонов и RTTI для конфигурации симулятора флеш-накопителя - Г...Использование шаблонов и RTTI для конфигурации симулятора флеш-накопителя - Г...
Использование шаблонов и RTTI для конфигурации симулятора флеш-накопителя - Г...
Yandex
 
особенности программирования на с++
особенности программирования на с++особенности программирования на с++
особенности программирования на с++
mcroitor
 
javascript_part1
javascript_part1javascript_part1
javascript_part1sovest
 
javascript
javascriptjavascript
javascriptsovest
 
паттерны программирования
паттерны программированияпаттерны программирования
паттерны программированияguestfc8ae0
 
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...
Yandex
 
DSLs in Lisp and Clojure
DSLs in Lisp and ClojureDSLs in Lisp and Clojure
DSLs in Lisp and Clojure
Vasil Remeniuk
 
Урок 1. Что такое функциональное программирование
Урок 1. Что такое функциональное программированиеУрок 1. Что такое функциональное программирование
Урок 1. Что такое функциональное программирование
Система дистанционного обучения MyDLS
 
20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonovComputer Science Club
 

Similar to Функциональное программирование с использованием библиотеки fp-ts | Odessa Frontend Meetup #19 (20)

Step cpp022
Step cpp022Step cpp022
Step cpp022
 
C# Desktop. Занятие 16.
C# Desktop. Занятие 16.C# Desktop. Занятие 16.
C# Desktop. Занятие 16.
 
Tech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVMTech Talks @NSU: Как приручить дракона: введение в LLVM
Tech Talks @NSU: Как приручить дракона: введение в LLVM
 
Как приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVMКак приручить дракона: введение в LLVM
Как приручить дракона: введение в LLVM
 
Спецкурс "Современные практики разработки ПО", 2013-2014 уч. год, занятие 2
Спецкурс "Современные практики разработки ПО", 2013-2014 уч. год, занятие 2Спецкурс "Современные практики разработки ПО", 2013-2014 уч. год, занятие 2
Спецкурс "Современные практики разработки ПО", 2013-2014 уч. год, занятие 2
 
Лекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building BlocksЛекция 8. Intel Threading Building Blocks
Лекция 8. Intel Threading Building Blocks
 
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++ Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
Юрий Ефимочев, Компилируемые в реальном времени DSL для С++
 
Иван Пузыревский — Введение в асинхронное программирование
Иван Пузыревский — Введение в асинхронное программированиеИван Пузыревский — Введение в асинхронное программирование
Иван Пузыревский — Введение в асинхронное программирование
 
Discovering Lambdas in Java 8
Discovering Lambdas in Java 8Discovering Lambdas in Java 8
Discovering Lambdas in Java 8
 
C++ Базовый. Занятие 03.
C++ Базовый. Занятие 03.C++ Базовый. Занятие 03.
C++ Базовый. Занятие 03.
 
вспомогательные алгоритмы
вспомогательные алгоритмывспомогательные алгоритмы
вспомогательные алгоритмы
 
Использование шаблонов и RTTI для конфигурации симулятора флеш-накопителя - Г...
Использование шаблонов и RTTI для конфигурации симулятора флеш-накопителя - Г...Использование шаблонов и RTTI для конфигурации симулятора флеш-накопителя - Г...
Использование шаблонов и RTTI для конфигурации симулятора флеш-накопителя - Г...
 
особенности программирования на с++
особенности программирования на с++особенности программирования на с++
особенности программирования на с++
 
javascript_part1
javascript_part1javascript_part1
javascript_part1
 
javascript
javascriptjavascript
javascript
 
паттерны программирования
паттерны программированияпаттерны программирования
паттерны программирования
 
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...
Евгений Крутько — Опыт внедрения технологий параллельных вычислений для повыш...
 
DSLs in Lisp and Clojure
DSLs in Lisp and ClojureDSLs in Lisp and Clojure
DSLs in Lisp and Clojure
 
Урок 1. Что такое функциональное программирование
Урок 1. Что такое функциональное программированиеУрок 1. Что такое функциональное программирование
Урок 1. Что такое функциональное программирование
 
20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov20130429 dynamic c_c++_program_analysis-alexey_samsonov
20130429 dynamic c_c++_program_analysis-alexey_samsonov
 

More from OdessaFrontend

Викторина | Odessa Frontend Meetup #19
Викторина | Odessa Frontend Meetup #19Викторина | Odessa Frontend Meetup #19
Викторина | Odessa Frontend Meetup #19
OdessaFrontend
 
Использование Recoil в React и React Native приложениях | Odessa Frontend Mee...
Использование Recoil в React и React Native приложениях | Odessa Frontend Mee...Использование Recoil в React и React Native приложениях | Odessa Frontend Mee...
Использование Recoil в React и React Native приложениях | Odessa Frontend Mee...
OdessaFrontend
 
Великолепный Gatsby.js | Odessa Frontend Meetup #19
Великолепный Gatsby.js | Odessa Frontend Meetup #19Великолепный Gatsby.js | Odessa Frontend Meetup #19
Великолепный Gatsby.js | Odessa Frontend Meetup #19
OdessaFrontend
 
Canvas API как инструмент для работы с графикой | Odessa Frontend Meetup #18
Canvas API как инструмент для работы с графикой | Odessa Frontend Meetup #18Canvas API как инструмент для работы с графикой | Odessa Frontend Meetup #18
Canvas API как инструмент для работы с графикой | Odessa Frontend Meetup #18
OdessaFrontend
 
Викторина | Odessa Frontend Meetup #17
Викторина | Odessa Frontend Meetup #17Викторина | Odessa Frontend Meetup #17
Викторина | Odessa Frontend Meetup #17
OdessaFrontend
 
Антихрупкий TypeScript | Odessa Frontend Meetup #17
Антихрупкий TypeScript | Odessa Frontend Meetup #17Антихрупкий TypeScript | Odessa Frontend Meetup #17
Антихрупкий TypeScript | Odessa Frontend Meetup #17
OdessaFrontend
 
Частые ошибки при разработке фронтенда | Odessa Frontend Meetup #17
Частые ошибки при разработке фронтенда | Odessa Frontend Meetup #17Частые ошибки при разработке фронтенда | Odessa Frontend Meetup #17
Частые ошибки при разработке фронтенда | Odessa Frontend Meetup #17
OdessaFrontend
 
OAuth2 и OpenID Connect простым языком | Odessa Frontend Meetup #17
OAuth2 и OpenID Connect простым языком | Odessa Frontend Meetup #17OAuth2 и OpenID Connect простым языком | Odessa Frontend Meetup #17
OAuth2 и OpenID Connect простым языком | Odessa Frontend Meetup #17
OdessaFrontend
 
Объекты в ECMAScript | Odessa Frontend Meetup #16
Объекты в ECMAScript | Odessa Frontend Meetup #16Объекты в ECMAScript | Odessa Frontend Meetup #16
Объекты в ECMAScript | Odessa Frontend Meetup #16
OdessaFrontend
 
Фриланс как профессиональная деградация | Odessa Frontend Meetup #16
Фриланс как профессиональная деградация | Odessa Frontend Meetup #16Фриланс как профессиональная деградация | Odessa Frontend Meetup #16
Фриланс как профессиональная деградация | Odessa Frontend Meetup #16
OdessaFrontend
 
Cлайдер на CSS | Odessa Frontend Meetup #16
Cлайдер на CSS | Odessa Frontend Meetup #16Cлайдер на CSS | Odessa Frontend Meetup #16
Cлайдер на CSS | Odessa Frontend Meetup #16
OdessaFrontend
 
Современный станок верстальщика
Современный станок верстальщикаСовременный станок верстальщика
Современный станок верстальщика
OdessaFrontend
 
Викторина | Odessa Frontend Meetup #15
Викторина | Odessa Frontend Meetup #15Викторина | Odessa Frontend Meetup #15
Викторина | Odessa Frontend Meetup #15
OdessaFrontend
 
DRY’им Vuex | Odessa Frontend Meetup #15
DRY’им Vuex | Odessa Frontend Meetup #15DRY’им Vuex | Odessa Frontend Meetup #15
DRY’им Vuex | Odessa Frontend Meetup #15
OdessaFrontend
 
А/Б тестирование: Что? Как? Зачем? | Odessa Frontend Meetup #15
А/Б тестирование: Что? Как? Зачем? | Odessa Frontend Meetup #15А/Б тестирование: Что? Как? Зачем? | Odessa Frontend Meetup #15
А/Б тестирование: Что? Как? Зачем? | Odessa Frontend Meetup #15
OdessaFrontend
 
Пощупать 3д в браузере | Odessa Frontend Meetup #15
Пощупать 3д в браузере | Odessa Frontend Meetup #15Пощупать 3д в браузере | Odessa Frontend Meetup #15
Пощупать 3д в браузере | Odessa Frontend Meetup #15
OdessaFrontend
 
Викторина | Odessa Frontend Meetup #14
Викторина | Odessa Frontend Meetup #14Викторина | Odessa Frontend Meetup #14
Викторина | Odessa Frontend Meetup #14
OdessaFrontend
 
Викторина | Odessa Frontend Meetup #13
Викторина | Odessa Frontend Meetup #13Викторина | Odessa Frontend Meetup #13
Викторина | Odessa Frontend Meetup #13
OdessaFrontend
 
Структуры данных в JavaScript | Odessa Frontend Meetup #13
Структуры данных в JavaScript | Odessa Frontend Meetup #13Структуры данных в JavaScript | Odessa Frontend Meetup #13
Структуры данных в JavaScript | Odessa Frontend Meetup #13
OdessaFrontend
 
Эффективность с большой буквы Э… или любой другой | Odessa Frontend Meetup #13
Эффективность с большой буквы Э… или любой другой | Odessa Frontend Meetup #13Эффективность с большой буквы Э… или любой другой | Odessa Frontend Meetup #13
Эффективность с большой буквы Э… или любой другой | Odessa Frontend Meetup #13
OdessaFrontend
 

More from OdessaFrontend (20)

Викторина | Odessa Frontend Meetup #19
Викторина | Odessa Frontend Meetup #19Викторина | Odessa Frontend Meetup #19
Викторина | Odessa Frontend Meetup #19
 
Использование Recoil в React и React Native приложениях | Odessa Frontend Mee...
Использование Recoil в React и React Native приложениях | Odessa Frontend Mee...Использование Recoil в React и React Native приложениях | Odessa Frontend Mee...
Использование Recoil в React и React Native приложениях | Odessa Frontend Mee...
 
Великолепный Gatsby.js | Odessa Frontend Meetup #19
Великолепный Gatsby.js | Odessa Frontend Meetup #19Великолепный Gatsby.js | Odessa Frontend Meetup #19
Великолепный Gatsby.js | Odessa Frontend Meetup #19
 
Canvas API как инструмент для работы с графикой | Odessa Frontend Meetup #18
Canvas API как инструмент для работы с графикой | Odessa Frontend Meetup #18Canvas API как инструмент для работы с графикой | Odessa Frontend Meetup #18
Canvas API как инструмент для работы с графикой | Odessa Frontend Meetup #18
 
Викторина | Odessa Frontend Meetup #17
Викторина | Odessa Frontend Meetup #17Викторина | Odessa Frontend Meetup #17
Викторина | Odessa Frontend Meetup #17
 
Антихрупкий TypeScript | Odessa Frontend Meetup #17
Антихрупкий TypeScript | Odessa Frontend Meetup #17Антихрупкий TypeScript | Odessa Frontend Meetup #17
Антихрупкий TypeScript | Odessa Frontend Meetup #17
 
Частые ошибки при разработке фронтенда | Odessa Frontend Meetup #17
Частые ошибки при разработке фронтенда | Odessa Frontend Meetup #17Частые ошибки при разработке фронтенда | Odessa Frontend Meetup #17
Частые ошибки при разработке фронтенда | Odessa Frontend Meetup #17
 
OAuth2 и OpenID Connect простым языком | Odessa Frontend Meetup #17
OAuth2 и OpenID Connect простым языком | Odessa Frontend Meetup #17OAuth2 и OpenID Connect простым языком | Odessa Frontend Meetup #17
OAuth2 и OpenID Connect простым языком | Odessa Frontend Meetup #17
 
Объекты в ECMAScript | Odessa Frontend Meetup #16
Объекты в ECMAScript | Odessa Frontend Meetup #16Объекты в ECMAScript | Odessa Frontend Meetup #16
Объекты в ECMAScript | Odessa Frontend Meetup #16
 
Фриланс как профессиональная деградация | Odessa Frontend Meetup #16
Фриланс как профессиональная деградация | Odessa Frontend Meetup #16Фриланс как профессиональная деградация | Odessa Frontend Meetup #16
Фриланс как профессиональная деградация | Odessa Frontend Meetup #16
 
Cлайдер на CSS | Odessa Frontend Meetup #16
Cлайдер на CSS | Odessa Frontend Meetup #16Cлайдер на CSS | Odessa Frontend Meetup #16
Cлайдер на CSS | Odessa Frontend Meetup #16
 
Современный станок верстальщика
Современный станок верстальщикаСовременный станок верстальщика
Современный станок верстальщика
 
Викторина | Odessa Frontend Meetup #15
Викторина | Odessa Frontend Meetup #15Викторина | Odessa Frontend Meetup #15
Викторина | Odessa Frontend Meetup #15
 
DRY’им Vuex | Odessa Frontend Meetup #15
DRY’им Vuex | Odessa Frontend Meetup #15DRY’им Vuex | Odessa Frontend Meetup #15
DRY’им Vuex | Odessa Frontend Meetup #15
 
А/Б тестирование: Что? Как? Зачем? | Odessa Frontend Meetup #15
А/Б тестирование: Что? Как? Зачем? | Odessa Frontend Meetup #15А/Б тестирование: Что? Как? Зачем? | Odessa Frontend Meetup #15
А/Б тестирование: Что? Как? Зачем? | Odessa Frontend Meetup #15
 
Пощупать 3д в браузере | Odessa Frontend Meetup #15
Пощупать 3д в браузере | Odessa Frontend Meetup #15Пощупать 3д в браузере | Odessa Frontend Meetup #15
Пощупать 3д в браузере | Odessa Frontend Meetup #15
 
Викторина | Odessa Frontend Meetup #14
Викторина | Odessa Frontend Meetup #14Викторина | Odessa Frontend Meetup #14
Викторина | Odessa Frontend Meetup #14
 
Викторина | Odessa Frontend Meetup #13
Викторина | Odessa Frontend Meetup #13Викторина | Odessa Frontend Meetup #13
Викторина | Odessa Frontend Meetup #13
 
Структуры данных в JavaScript | Odessa Frontend Meetup #13
Структуры данных в JavaScript | Odessa Frontend Meetup #13Структуры данных в JavaScript | Odessa Frontend Meetup #13
Структуры данных в JavaScript | Odessa Frontend Meetup #13
 
Эффективность с большой буквы Э… или любой другой | Odessa Frontend Meetup #13
Эффективность с большой буквы Э… или любой другой | Odessa Frontend Meetup #13Эффективность с большой буквы Э… или любой другой | Odessa Frontend Meetup #13
Эффективность с большой буквы Э… или любой другой | Odessa Frontend Meetup #13
 

Функциональное программирование с использованием библиотеки fp-ts | Odessa Frontend Meetup #19

  • 2. Обо мне ● Программирую с 2014 года ● Активно применяю функциональный подход на текущем проекте Дмитрий Ховрич Software Engineer в
  • 4. Функциональное программирование Функциональное программирование - это программирование с использованием чистых математических функций
  • 5. Зачем писать код в функциональном стиле? Функциональное программирование, как и любая другая парадигма, накладывает дополнительные ограничения и правила. Соблюдение этих ограничений и правил позволяет писать более надежный код, который легче тестировать и повторно использовать.
  • 6. Как мы можем себя ограничить во время написания кода? “Хард” ограничения ● Компилятор TypeScript ○ Максимально строгий режим ○ TypeScript: Раскладываем tsconfig по полочкам. Часть 1 ○ TypeScript: Раскладываем tsconfig по полочкам. Часть 2 — Всё про строгость ● ESLint “Софт” ограничения ● Парадигма программирования ● Паттерны и лучшие практики ● Код ревью
  • 7. Библиотека fp-ts Библиотека fp-ts позволяет писать чистые функциональные приложения, построенные поверх высокоуровневых абстракций из таких языков, как Haskell, PureScript, и Scala. ● Паттерны и надежные абстракции из функционального мира ● Утилиты для композиции функций ● Функциональные типы данных ● Классы типов (type classes)
  • 8. Класс типов Класс типов определяет множество типизированных функций и констант, которые должны существовать для каждого типа, который принадлежит данному классу. Основаны на теории категорий
  • 9. Теория категорий Теория категорий — раздел математики, изучающий свойства отношений между математическими объектами, не зависящие от внутренней структуры объектов.
  • 11. Теория категорий Вам не нужно изучать теорию категорий для того, чтобы использовать функциональный подход в Ваших программах!
  • 12. Класс типов Show interface Show<A> { readonly show: (a: A) => string; } Тип A принадлежит классу Show, если для A определена функция show : (a: A) => string
  • 13. Реализации класса типов Show const showNumber: Show<number> = { show: n => n.toString() }; const showUser: Show<User> = { show: user => `User "${user.name}", ${user.age} years old` };
  • 14. Из чего состоит функциональное программирование?
  • 15. Из чего состоит функциональное программирование? Концепции, которые “лежат на поверхности” Концепции, которые “спрятаны поглубже”
  • 16. Концепции, которые “лежат на поверхности” ● Иммутабельные данные ● Чистые функции (referentially transparent) ○ Каррирование ● Побочные эффекты ● “Честные” сигнатуры функций (method signature honesty) ● Композиция функций (бесточечный стиль)
  • 17. Иммутабельные данные ● Изменение данных путем пересоздания объектов / массивов ● TypeScript не поддерживает иммутабельные типы данных в runtime ○ Но скоро могут появятся записи (records) и кортежи (tuples) ● Ключевое слово readonly ○ ReadonlyArray ○ ReadonlyRecord ○ ReadonlyMap ○ ReadonlySet ● eslint-plugin-functional
  • 18. Иммутабельные данные const arr: readonly number[] = [1, 2, 3]; arr.push(4); // Property 'push' does not exist on type 'readonly number[]' arr.pop(); // Property 'pop' does not exist on type 'readonly number[]'. arr.unshift(); // Property 'unshift' does not exist on type 'readonly number[]'. arr.shift(); // Property 'shift' does not exist on type 'readonly number[]' arr.splice(1); // Property 'splice' does not exist on type 'readonly number[]'
  • 19. Чистые функции const sum = (a: number, b: number): number => a + b; const multiply = (a: number, b: number): number => a * b; const subtract = (a: number, b: number): number => a - b; const divide = (a: number, b: number): number => a / b; const dateToString = (date: Date): string => date.toDateString();
  • 20. Чистые каррированые функции const sum = (a: number) => (b: number): number => a + b; const multiply = (a: number) => (b: number): number => a * b; const byEmail = (email: string) => (user: User): boolean => user.email === email; // Удобно применять когда функция ожидает предикат на вход const user = users.find(byEmail("peter-parker@gmail.com"));
  • 21. Чистые функции могут мутировать данные внутри себя function shuffle<T>(array: readonly T[]): readonly T[] { const arrayCopy = [...array]; for (let i = arrayCopy.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); const temp = arrayCopy[i]; arrayCopy[i] = array[j]; arrayCopy[j] = temp; } return arrayCopy; }
  • 22. Побочные эффекты Побочный эффект - это изменение состояния системы или наблюдаемое взаимодействие с окружающим миром, происходящее во время вычисления результата. ● изменения в файловой системе ● обращение к базе данных ● выполнение http-запроса ● выброс ошибок ● вывод на экран / запись в лог ● получение данных от пользователя ● выполнение запроса к DOM Любое взаимодействие со средой вне функции является побочным эффектом.
  • 23. Функции с побочными эффектами const getCurrentDateIso = (): string => new Date().toISOString(); const log = <T>(key: string, value: T): void => console.log(`${key}: ${value}`); const saveToStorage = <T>(key: string, value: T): void => { localStorage.setItem(key, JSON.stringify(value)); }; const getFromStorage = (key: string): string | null => localStorage.getItem(key);
  • 24. “Честные” сигнатуры функций type User = { readonly id: string; readonly email: string; }; declare function createUser(email: string): User;
  • 25. Скрытые побочные эффекты внутри функции function createUser(email: string): User { if (email.length > 256) { throw new Error("Email is too long!"); } if (!email.includes("@")) { throw new Error("Email is invalid!"); } return { id: uuid(), email }; }
  • 27. Последовательные вызовы без композиции const value = 2; const valueAfterSum = sum(value, 2); const valueAfterMultiply = multiply(valueAfterSum, 3); const result = valueToString(valueAfterMultiply); console.log(valueString); // 12
  • 28. Неудобная композиция функций const result = valueToString(multiply(sum(value, 2), 3)); console.log(result); // 12
  • 29. Композиция функций с применением pipe import {pipe} from "fp-ts/function"; const value = 2; const result = pipe(value, sum(2), multiply(3), valueToString); console.log(result); // 12
  • 30. Как дебажить? Логами const debug = <T>(key: string) => (value: T): T => { console.log(`DEBUG:${key} : ${value}`); return value; }
  • 31. Как дебажить? Логами const result = pipe( value, sum(2), debug("sum"), // DEBUG:sum : 4 multiply(3), debug("multiply"), // DEBUG:multiply : 12 valueToString ); console.log(result); // 12
  • 32. Как дебажить? Логами Используйте ESLint правило no-restricted-imports для того, чтобы “отловить” забытые debug функции на этапе git commit или CI
  • 33. А что может пойти не так?
  • 34. declare function getUsers(): readonly User[]; declare function createFullName(user: User): string; const users = getUsers(); const result = pipe( users.find(byEmail("spider-man@gmail.com")), createFullName ); Проблема композиции функций. Код скомпилируется?
  • 35. Проблема композиции функций // Argument of type 'User | undefined' is not assignable to parameter of type 'User'. // Type 'undefined' is not assignable to type 'User'. const result = pipe( users.find(byEmail("spider-man@gmail.com")), createFullName );
  • 37. Объединения типов с дискриминантом
  • 38. Объединения типов с дискриминантом Объединения типов с дискриминантом - это объединения у которых присутствует одно общее поле (с уникальным символом, чаще всего строковым литералом). Оно и будет служить дискриминантом, чтобы TypeScript по нему вывел что же за тип у вас сейчас в руках.
  • 39. Объединения типов с дискриминантом type Success<T> = { readonly type: "success"; readonly value: T; } type Fail = { readonly type: "fail"; readonly error: Error; } type Result<T> = Success<T> | Fail;
  • 40. Объединения типов с дискриминантом declare function loadUsers(): Result<readonly User[]>; const result = loadUsers(); // Property 'value' does not exist on type 'Result<readonly User[]>' console.log(result.value); // Property 'error' does not exist on type 'Result<readonly User[]>' console.log(result.error);
  • 41. Объединения типов с дискриминантом switch (result.type) { case "success": console.log(result.value); break; case "fail": console.log(result.error); break; default: absurd(result); // typeof result is never }
  • 42. Тело функции absurd function absurd<A>(_: never): A { throw new Error('Called `absurd` function which should be uncallable') }
  • 44. Option Option - конструкция, описывающая наличие или отсутствие значения. С помощью функций map и chain позволяет работать с цепочками вызовов функций даже если какая-то из них может вернуть null / undefined. Option - тип данных, предоставляемый библиотекой fp-ts ● Описание типа ● Функции для взаимодействия
  • 45. Описание типа Option type None = { readonly _tag: 'None'; }; type Some<A> = { readonly _tag: 'Some'; readonly value: A; }; type Option<A> = None | Some<A>;
  • 47. ReadonlyArray - head declare const head: <A>(as: readonly A[]) => Option<A>
  • 48. ReadonlyArray - last declare const last: <A>(as: readonly A[]) => Option<A>
  • 49. ReadonlyArray - lookup declare const lookup: (i: number) => <A>(as: ReadonlyArray<A>) => Option<A>
  • 50. ReadonlyArray - findFirst declare const findFirst: <A>(predicate: Predicate<A>) => (as: ReadonlyArray<A>) => Option<A>
  • 52. Пример использование Option import {findFirst} from "fp-ts/ReadonlyArray"; // Option<User> // { _tag: 'Some', value: { id: 1, email: "spiderman@gmail.com" } } const value = pipe( users, findFirst(byEmail("spider-man@gmail.com")) );
  • 53. Пример использование Option import { map } from "fp-ts/Option"; // Option<string> // { _tag: 'Some', value: 'Peter Parker' } const value = pipe( users, findFirst(byEmail("spider-man@gmail.com")), map(createFullName) );
  • 54. Пример использование Option import { map } from "fp-ts/Option"; // Option<string> // { _tag: 'None' } const value = pipe( users, findFirst(byEmail("mary-jane-watson@gmail.com")), map(createFullName) );
  • 55. Описание типа функции Map для Option type OptionMap = <A, B>(f: (a: A) => B) => (fa: Option<A>) => Option<B>;
  • 56. Реализация функции Map для Option const map: OptionMap = f => option => { switch (option._tag) { case "None": return option; case "Some": return { _tag: "Some", value: f(option.value) }; default: return absurd(option) } }
  • 57. map-ов может быть много declare function stringHash(value: string): number; // Option<number> const value = pipe( users, findFirst(byEmail("spider-man@gmail.com")), map(createFullName), map(stringHash) );
  • 58. Функтор — это класс типов, для которых определена функция map и соблюдаются некоторые законы: ● Закон идентичности ● Закон композиции Законы - это ограничения, которые накладываются на реализацию функции map. В ежедневной работе Вам не придется писать свои функторы. Все необходимые функторы реализованы в библиотеке fp-ts. Функтор
  • 59. Вложенные Option<T> - плохая практика import {lookup} from "fp-ts/Record"; const comments: Record<UserId, readonly Comment[]> = {}; // Option<Option<readonly Comment[]>> const value = pipe( users, findFirst(byEmail("spider-man@gmail.com")), map(user => lookup(user.id, comments)) )
  • 60. Record - lookup declare const lookup: <A>(k: string, r: Record<string, A>): Option<A>
  • 61. Как избавиться от вложенных Option<T> import {chain} from "fp-ts/ReadonlyArray"; const comments: Record<UserId, readonly Comment[]> = {}; // Option<readonly Comment[]> const value = pipe( users, findFirst(byEmail("spider-man@gmail.com")), chain(user => lookup(user.id, comments)) )
  • 62. Описание типа функции Chain для Option type OptionChain = <A, B>(f: (a: A) => Option<B>) => (fa: Option<A>) => Option<B>;
  • 63. Реализация функции Chain для Option const chain: OptionChain = f => option => { switch (option._tag) { case "None": return option; case "Some": return f(option.value); default: return absurd(option) } }
  • 64. Монада Монада - это класс типов, для которой определены функции map, chain, of и соблюдаются некоторые законы: ● Закон идентичности ● Закон ассоциативности Законы - это ограничения, которые накладываются на реализацию функции chain. В ежедневной работе Вам не придется писать свои монады. Все необходимые монады реализованы в библиотеке fp-ts. Монады используются для последовательных вычислений.
  • 66. Как достать значение из Option? import {toUndefined} from "fp-ts/Option"; // string | undefined const value = pipe( users, findFirst(byEmail("spider-man@gmail.com")), map(createFullName), toUndefined );
  • 67. Как достать значение из Option? import {constant} from "fp-ts/function"; import {getOrElse} from "fp-ts/Option"; // string const value = pipe( users, findFirst(byEmail("spider-man@gmail.com")), map(createFullName), getOrElse(constant<string>("NO_NAME")) );
  • 68. Что еще интересного есть в fp-ts? ● Either - для работы с ошибками ● IO - для работы с побочными эффектами ● Task - для работы с асинхронным кодом ● TaskEither - для работы с асинхронным кодом, который может вернуть ошибку ● Reader - внедрение зависимостей в функциональном стиле ● State - для хранения состояния в композиции функций ● ReaderTaskEither - внедрение зависимостей в асинхронную функцию, которая может вернуть ошибку ● Eq - для сравнения данных по значению ● Ord - для сортировки данных ● Semigroup - для конкатенации данных ● Monoid - для “сворачивания” данных (reduce)
  • 69. Что почитать? Код из презентации https://github.com/dkhovrich/fp-ts-talk-code#readme
  • 70. Где пообщаться / попросить о помощи? https://t.me/fp_ts
  • 71. Вакансия в команду Spark ● продуктовая компания и возможность влиять на сам продукт ● команда профессионалов ● стек технологий (TypeScript, React, RxJS, fp-ts) ● пробуем новые инструменты, подходы ● достойный компенсационный пакет от компании ● гибридный гибкий график (офис, ремоут) Все вакансии Readdle - https://readdle.com/careers 🚀