C++ CoreHard Autumn 2018. Обработка списков на C++ в функциональном стиле - В...corehard_by
Язык C++, претерпев долгую эволюцию, обрёл ряд черт, характерных для функциональной парадигмы: функции стали полноправными объектами, над которыми могут выполняться операции, а аппарат шаблонов позволяет проводить вычисления на типах на этапе компиляции. Математический фундамент этих двух главных аспектов составляют, соответственно, ламбда-исчисление и теория категорий. Расширение языка этими средствами способствовало реализации на языке C++ ряда инструментов, известных из функционального программирования. Некоторые из этих реализаций вошли в стандартную библиотеку (std::function, std::bind), другие - в сторонние библиотеки, в том числе в коллекцию библиотек Boost (functional, hana). Важную роль в арсенале функционального программирования играют операции свёртки и развёртки, которые очевиднее всего определяются для списков, но также естественным образом обобщаются на другие индуктивные и коиндуктивные структуры данных. Например, суммирование списка чисел можно представить себе как свёртку списка по операции сложения, а построение списка простых множителей заданного целого числа - как развёртку. Обобщения свёртки и развёртки известны как анаморфизмы и катаморфизмы. Также в функциональном программировании находит применение понятие гиломорфизма - композиция развёртки некоторого объекта в коллекцию с последующей свёрткой её в новый объект. В докладе продемонстрировано, что свёртки, развёртки и их композиции допускают довольно простую реализацию на языке C++.
Нас окружает мир сетей, мобильных устройств, сайтов, облаков. Чтобы работать с этим миром, придумано невероятное количество технологий и языков программирования. Есть ли среди них место для языков Си/Си++? Стоит ли тратить время на их изучение, стоит ли использовать их в своих проектах? Не пора ли этим языкам на пенсию? Эти темы в своем докладе обсудит Андрей Карпов, активно участвующий в жизни сообщества Си++-программистов. Забегая вперед можно утверждать - языки Си/Си++ живее всех живых. Андрей расскажет о развитии языка и новых возможностях, появившихся в Си++11. Многие возможности существенно облегчают работу программиста и сокращают объем кода.
Слайды по представлению графов в памяти компьютера. Примеры кода на языке C++. Акценты расставлены на дообъектных представлениях, которые могут быть использованы школьниками при решении олимпиадных задач по программированию.
This document discusses the program development cycle and different programming paradigms. The program development cycle includes steps like analysis, design, coding, testing and debugging, and documentation. It then defines four major programming paradigms: imperative, functional, logic, and object-oriented. Each paradigm is described in terms of its approach, examples of languages that use it, and differences from the other paradigms.
The document discusses the process of writing a computer program. It explains that programming involves breaking a problem down into a logical sequence of steps. There are two main phases: the problem-solving phase where the problem is analyzed and an algorithm is developed, and the implementation phase where the algorithm is translated into a programming language and tested. The process also includes a maintenance phase to modify the program as needed over time.
C++ CoreHard Autumn 2018. Обработка списков на C++ в функциональном стиле - В...corehard_by
Язык C++, претерпев долгую эволюцию, обрёл ряд черт, характерных для функциональной парадигмы: функции стали полноправными объектами, над которыми могут выполняться операции, а аппарат шаблонов позволяет проводить вычисления на типах на этапе компиляции. Математический фундамент этих двух главных аспектов составляют, соответственно, ламбда-исчисление и теория категорий. Расширение языка этими средствами способствовало реализации на языке C++ ряда инструментов, известных из функционального программирования. Некоторые из этих реализаций вошли в стандартную библиотеку (std::function, std::bind), другие - в сторонние библиотеки, в том числе в коллекцию библиотек Boost (functional, hana). Важную роль в арсенале функционального программирования играют операции свёртки и развёртки, которые очевиднее всего определяются для списков, но также естественным образом обобщаются на другие индуктивные и коиндуктивные структуры данных. Например, суммирование списка чисел можно представить себе как свёртку списка по операции сложения, а построение списка простых множителей заданного целого числа - как развёртку. Обобщения свёртки и развёртки известны как анаморфизмы и катаморфизмы. Также в функциональном программировании находит применение понятие гиломорфизма - композиция развёртки некоторого объекта в коллекцию с последующей свёрткой её в новый объект. В докладе продемонстрировано, что свёртки, развёртки и их композиции допускают довольно простую реализацию на языке C++.
Нас окружает мир сетей, мобильных устройств, сайтов, облаков. Чтобы работать с этим миром, придумано невероятное количество технологий и языков программирования. Есть ли среди них место для языков Си/Си++? Стоит ли тратить время на их изучение, стоит ли использовать их в своих проектах? Не пора ли этим языкам на пенсию? Эти темы в своем докладе обсудит Андрей Карпов, активно участвующий в жизни сообщества Си++-программистов. Забегая вперед можно утверждать - языки Си/Си++ живее всех живых. Андрей расскажет о развитии языка и новых возможностях, появившихся в Си++11. Многие возможности существенно облегчают работу программиста и сокращают объем кода.
Слайды по представлению графов в памяти компьютера. Примеры кода на языке C++. Акценты расставлены на дообъектных представлениях, которые могут быть использованы школьниками при решении олимпиадных задач по программированию.
This document discusses the program development cycle and different programming paradigms. The program development cycle includes steps like analysis, design, coding, testing and debugging, and documentation. It then defines four major programming paradigms: imperative, functional, logic, and object-oriented. Each paradigm is described in terms of its approach, examples of languages that use it, and differences from the other paradigms.
The document discusses the process of writing a computer program. It explains that programming involves breaking a problem down into a logical sequence of steps. There are two main phases: the problem-solving phase where the problem is analyzed and an algorithm is developed, and the implementation phase where the algorithm is translated into a programming language and tested. The process also includes a maintenance phase to modify the program as needed over time.
The document discusses computer programming fundamentals. It explains that computers are "dumb" machines that need step-by-step instructions to perform tasks, and that programming involves providing these instructions through computer programs. It describes how early programs had to be written in binary machine language, which was difficult for humans, leading to the development of assembly language and high-level languages that are easier for people to use. These higher-level languages are converted into machine-readable format through assemblers and compilers.
This document provides an introduction to general object-oriented programming concepts. It discusses that OOP offers a powerful way to develop software by creating objects that encapsulate both data and functions. The core concepts of OOP explained are objects, classes, encapsulation, inheritance, polymorphism, and message passing. Objects communicate by sending and receiving messages, and classes act as templates for creating object instances that share common properties.
This slide about presentation of Object Oriented Programing or OOP contains Fundamental of OOP
Encapsulation
Inheritance
Abstract Class
Association
Polymorphism
Interface
Exceptional Handling
and more.
The document discusses several key aspects of programming languages including:
1) There is amazing variety across languages with over 2300 published languages grouped into four main families: imperative, functional, logic, and object-oriented.
2) Programming languages are the subject of ongoing debates around their relative merits and definitions.
3) Languages are constantly evolving as new ideas are introduced and older languages develop new dialects.
4) Languages influence programming practices but programmers can also work against a language's favored style.
This document discusses computer hardware, software, programming languages, and how code is executed. It defines hardware as physical components like the motherboard, disk drive, and fans. Software consists of programs made of instruction sequences that hardware can understand. Programming languages have evolved from low-level machine code and assembly code to high-level languages like C, C++, Java, and PHP. Language translators like assemblers, compilers, and interpreters are used to translate source code into executable machine code that computers can understand.
The document describes the evolution of programming languages from machine languages to high-level languages, and how a program written in a high-level language is translated into machine language. It discusses four main programming paradigms - procedural, object-oriented, functional, and declarative - and provides details on the procedural paradigm. Key aspects of the procedural paradigm include programs made up of procedures that manipulate passive data items, and common procedural languages like FORTRAN, COBOL, Pascal, C, and Ada.
C is a general-purpose programming language developed in the 1970s. The lecture discusses C's history and uses, and outlines the typical program development cycle of requirement analysis, design, implementation, testing, and documentation. It also introduces common programming tools like algorithms, pseudocode, flowcharts, and hierarchy charts to plan and design programs before coding.
A programming language is a set of rules that allows humans to tell computers what operations to perform. Programming languages provide tools for developing executable models for problem domains and exist at various levels from high-level languages that are closer to human language to low-level machine code. Some of the principal programming paradigms include imperative, object-oriented, logic/declarative, and functional programming. Popular high-level languages include FORTRAN, COBOL, BASIC, C, C++, Java, and markup languages like HTML and XML.
Functional Programming Patterns (NDC London 2014)Scott Wlaschin
(video of these slides available here http://fsharpforfunandprofit.com/fppatterns/)
In object-oriented development, we are all familiar with design patterns such as the Strategy pattern and Decorator pattern, and design principles such as SOLID.
The functional programming community has design patterns and principles as well.
This talk will provide an overview of some of these, and present some demonstrations of FP design in practice.
The document discusses key concepts in object-oriented programming including objects, classes, messages, and requirements for object-oriented languages. An object is a bundle of related variables and methods that can model real-world things. A class defines common variables and methods for objects of a certain kind. Objects communicate by sending messages to each other specifying a method name and parameters. For a language to be object-oriented, it must support encapsulation, inheritance, and dynamic binding.
(video of these slides available here http://fsharpforfunandprofit.com/fppatterns/)
In object-oriented development, we are all familiar with design patterns such as the Strategy pattern and Decorator pattern, and design principles such as SOLID.
The functional programming community has design patterns and principles as well.
This talk will provide an overview of some of these, and present some demonstrations of FP design in practice.
Николай Паламарчук "Functional Programming basics for PHP developers"Fwdays
Functional Programming becomes very popular nowadays. What is it? Is it a hype or panacea? Should you deal with it as a PHP programmer? Let's find out!
Olexandra Dmytrenko
QA Automating at EPAM Systems
I'll show you how to switch from writing standard code using good old Java7 into writing it using functional way presented in Java8. The training is counted on beginners in the subject who like discovering the new horizons or for those who want to become more firm in using the new lambda features.
Евгений Рыжков, Андрей Карпов Как потратить 10 лет на разработку анализатора ...Platonov Sergey
Кто-то верно подметил, что разработчики статических анализатора часто сталкиваются с "проблемой айсберга". Им сложно объяснить разработчикам, почему сложно написать и развивать статические анализаторы кода. Дело в том, что сторонние наблюдатели видят только вершину всего процесса, так как им доступен для изучения только простой интерфейс, который предоставляют анализаторы для взаимодействия с миром. Это ведь не графический редактор с сотнями кнопок и рычажков. В результате и возникает ощущение, что раз прост интерфейс взаимодействия, то и прост продукт. На самом деле статические анализаторы кода — это сложные программы, в которых живут и взаимодействуют разнообразнейшие методы поиска дефектов. В них реализуется множество экспертные системы, выдающие заключения о коде на основе как точных, так и эмпирических алгоритмах. В парном докладе, основатели анализатора PVS-Studio расскажут о том, как незаметно потратить 10 лет, чтобы написать хороший анализатор. Дьявол кроется в деталях!
Алексей Куканов — Параллелизм в C++: управляйте приложением, а не потоками!Yandex
Алексей Куканов, Intel.
Последняя версия стандарта С++ добавляет в язык и библиотеку поддержки средства для использования потоков исполнения (threads) и синхронизации между ними. Однако это лишь необходимая низкоуровневая база для внедрения параллелизма. Эффективная разработка параллельных программ требует высокоуровневого API, реализующего типичные шаблоны использования параллелизма в виде, пригодном для применения в широком спектре алгоритмов и приложений. В докладе речь пойдёт о наиболее часто встречающихся параллельных шаблонах, реализованных в программных моделях Intel® Threading Building Blocks и Intel® Cilk Plus, и о примерах их использования.
Tech Talks @NSU: Back to the Future: Функциональное программирование вчера и ...Tech Talks @NSU
http://techtalks.nsu.ru
Видеозапись: http://www.youtube.com/watch?v=f6Xk-CLUcF4
05 ноября 2014. Back to the Future: Функциональное программирование вчера и сегодня.(Александр Гранинр, Лаборатория Касперского)
«Несмотря на то, что функциональное программирование существует уже 60 лет, до сих пор оно оставалось в тени императивного программирования, а разработчики о нем знали лишь потому, что в университетах иногда есть некая туманная дисциплина с таким названием. Но сегодня, когда стало ясно, что ООП не является панацеей в борьбе со сложностью ПО, о функциональном программировании внезапно вспомнили: оказалось, что идеи, которые оно предлагает, помогают решать традиционные задачи более простым и элегантным способом. Современные мэйнстримные языки, в развитии своем исчерпав все лимиты из ООП и императивного программирования, двинулись в сторону функционального программирования. В лекции будет рассказано о наиболее значимых идеях и принципах, которые вышли из мира функционального программирования и с успехом покоряют умы современных мэйнстрим-разработчиков.»
Лекция прочитана в рамках проекта Tech Talks @NSU – серии открытых лекций о разработке ПО и карьере в IT, проводимых в Новосибирском государственном университете.
Подробности: http://techtalks.nsu.ru
Back to the Future: Функциональное программирование вчера и сегодняTech Talks @NSU
http://techtalks.nsu.ru
Видеозапись: http://www.youtube.com/watch?v=f6Xk-CLUcF4
Александр Гранин (Лаборатория Касперского) рассказывает о функциональном программировании.
«Несмотря на то, что функциональное программирование существует уже 60 лет, до сих пор оно оставалось в тени императивного программирования, а разработчики о нем знали лишь потому, что в университетах иногда есть некая туманная дисциплина с таким названием. Но сегодня, когда стало ясно, что ООП не является панацеей в борьбе со сложностью ПО, о функциональном программировании внезапно вспомнили: оказалось, что идеи, которые оно предлагает, помогают решать традиционные задачи более простым и элегантным способом. Современные мэйнстримные языки, в развитии своем исчерпав все лимиты из ООП и императивного программирования, двинулись в сторону функционального программирования.
В лекции будет рассказано о наиболее значимых идеях и принципах, которые вышли из мира функционального программирования и с успехом покоряют умы современных мэйнстрим-разработчиков».
Лекция прочитана в рамках проекта Tech Talks @NSU – серии открытых лекций о разработке ПО и карьере в IT, проводимых в Новосибирском государственном университете.
Подробности: http://techtalks.nsu.ru
модель акторов и C++ что, зачем и как ?corehard_by
Модель акторов, переживающая сейчас очередную волну популярности, является очень интересным подходом к разработке сложных приложений. С помощью модели акторов было создано множество систем, написанных на языке Erlang и на базе фреймворка Akka. Но Erlang и Akka -- это управляемые среды и безопасные языки программирования. А есть ли смысл применять модель акторов в C++? Если есть, то куда смотреть и что использовать? Какие подводные камни могут поджидать на этом пути? Об этом всем и пойдет речь в докладе.
2. ФП появилось раньше ООП и структурного
программирования
LISP 1958
Закон Мура 1965
Simula 1967
Структурное программирование 1970
ML 1973
С++ 1983
Ocaml 1985
Erlang 1987
Haskell 1990
Scala 2003
F# 2005
Clojure 2007
Elm 2012
More than Moore 2014
3. Но не получило распространения в индустрии
из-за дороговизны памяти на заре computer
science, хотя и нашло свое место в научных
кругах
4. Интерес вернулся в последние 5 лет
• Закон Мура «выдыхается»
• Популярность облачных
решений растет
• Big Data, машинное обучение,
боты, когнитивные сервисы
5. LISP
• Появился в 1958 году
• Создатель Лиспа Джон Маккарти занимался исследованиями в
области искусственного интеллекта (в дальнейшем ИИ) и
созданный им язык по сию пору является одним из основных
средств моделирования различных аспектов ИИ
• Это один из старейших (наряду с Фортраном и Коболом)
используемых по сей день высокоуровневых языков
программирования
• Основой архитектуры его является лямбда-исчисление
• Интерпретатор Лиспа, написанный на Лиспе, занимает 15 строк
6. Что не так с состоянием?
• Locking, Memory Bandwidth
• How To Reproduce / Debug
• Race Conditions
• Side Effects
• Не возможно доказать корректность программы
7. Чему равен y?
var x = 2;
DoSomething(x);
// чему равен y?
var y = x - 1;
10. Функциональное программиирование
• Раздел дискретной математики
и парадигма
программирования, в которой
процесс вычисления трактуется
как вычисление значений
функций в математическом
понимании последних
• Понимание функции отличается
от функции как подпрограммы
в процедурном
программировании
11. Функция –
Соответствие между элементами двух множеств, установленное по
такому правилу, что каждому элементу одного множества ставится
в соответствие некоторый элемент из другого множества.
12. Принцип разделения интерфейса
Принцип разделения интерфейсов говорит о том, что слишком
«толстые» интерфейсы необходимо разделять на более маленькие
и специфические, чтобы клиенты маленьких
public interface IMessyCalc
{
float Add(float a, float b);
Rocket LaunchRocket(); // WAT?
}
13. Принцип разделения интерфейса
public interface ICalc
{
float Add(float a, float b);
}
public interface IRocketLauncher
{
Rocket LaunchRocket(); // ok
}
15. Функции и вывод типов
let add1 x = x + 1 // int -> int
let add2 x = x + 2 // int -> int
let add3C = add1 >> add2 //композиция
let add3P x = x |> add1 |> add2 // pipe-оператор
let toString x = x.ToString() // 'a -> string
19. Каррирование и частичное применение
let printTwoParameters x y =
printfn "x=%i y=%i" x y
//explicitly curried version
let printTwoParametersC x = // only one parameter!
let subFunction y =
printfn "x=%i y=%i" x y // new function with one param
subFunction
let add x y = x + y
let add1P = add 1
let v = add1P 2
let result =
[1..10]
|> List.map (fun i -> i+1)
|> List.filter (fun i -> i>5)
22. λ-исчисление
• Формальная система, разработанная американским математиком
Алонзо Чёрчем, для формализации и анализа понятия
вычислимости
• Строится на простых двух операциях: аппликации и абстракции
• Определены фундаментальные понятия α-эквивалентностью и β-
редукции
• Многие функциональные языки можно рассматривать как
"надстройку" над ними
23. λ-исчисление
• Нет циклов
• Нет переменных
• Нет операции присвоения
• Нет изменяемого состояния
• Нет условных переходов и циклов
• Нет «обычной» рекурсии
• Обладает свойством полноты по
Тьюрингу и, следовательно,
представляет собой простейший язык
программирования
24. Теорема о неподвижной точке
• И в λ-исчислении, и в комбинаторной логике для каждого терма X
существует по крайней мере один терм P такой, что XP = P. И
более того, существует комбинатор Y такой, что YX = X(YX)
• Комбинатор неподвижной точки преобразует нерекурсивную
функцию, вычисляющую один шаг рекурсии, в рекурсивную
• Y = λf.(λx.f(xx))(λx.f(xx))
• Z = λf.(λx.f(λy.xxy))(λx.f(λy.xxy))
• https://habrahabr.ru/post/79713/
25. α-эквивалентность
• λx.x и λy.y альфа-эквивалентные лямбда-термы и оба
представляют одну и ту же функцию (функцию тождества).
• Термы x и y не альфа-эквивалентны, так как они не находятся в
лямбда абстракции
27. Структурирование программ
Expressions VS Statements
Expression<Func<int, string>> expr = x =>
{
return x.ToString();
};
CS0834 A lambda expression with a statement body cannot be
converted to an expression tree
28. Statement
var str = "test";
var i = 1;
if (flag)
{
str += i;
}
else
{
i++;
}
31. Что возвращает функция?
function DoSomething (foo) {
if (foo > 5) return 2;
throw "Error";
}
var x = 2;
DoSomething(x);
var y = x - 1;
32. Exception Handling Considered Harmful
• Hidden Control Flow & Corrupt State
• Mismatch With Parallel Programming
• Exceptional Exceptions
• Exceptions only really work reliably when nobody
catches them
33. do
{
using (var lifetimeScope = _container.BeginLifetimeScope())
{
var billingService = lifetimeScope.Resolve<BillingService>();
try
{
int activityCount = billingService.ProcessNotRatedActivities();
if (activityCount > 0)
{
Logger.Instance.Info($"Handled {activityCount} pending rated activities");
}
}
catch (Exception exception)
{
Logger.Instance.Error(exception, CultureInfo.CurrentCulture,
"An error occured while handling pending rated activities");
}
}
} while (!cancellationToken.WaitHandle.WaitOne(_pollingInterval));
39. Алгебраические типы данных
type Option<'T> =
| Some of 'T
| None
type Season = Autumn | Winter | Spring | Summer
type Profile = {
FirstName: string
LastName: string
}
40. Pattern Matching
let printSomeOrNone x =
match x with
| Some(y) -> printfn "some: %A" y
| None -> printfn "none"
| _ -> printfn "Never hit"
// only use if you have to
58. Определение порядковых чисел по фон
Нейману
• Множество S является ординалом тогда и только тогда, когда оно
строго вполне упорядочено отношением «принадлежать» и
каждый элемент S одновременно является его подмножеством
• Любой ординал есть вполне упорядоченное множество,
состоящее из всех ординалов, меньших его
59.
60. Основы теории категорий
• В программах, использующих операционную семантику, очень трудно
что-то доказать. Чтобы показать некое свойство программы вы, по
сути, должны «запустить ее» через идеализированный интерпретатор
• Но есть и альтернатива. Она называется денотационной семантикой и
основана на математике. В денотационной семантике для каждой
языковой конструкции описана математичесая интерпретация
• По сравнению с теоремами, которые доказывают профессиональные
математики, задачи, с которыми мы сталкиваемся в
программировании, как правило, довольно просты, если не
тривиальны
61. Какова математическая модель для чтения
символа с клавиатуры, или отправки пакета по
сети?
• Долгое время это был бы неловкий вопрос, ведущий к довольно
запутанным объяснениям. Казалось, денотационная семантика
не подходит для значительного числа важных задач, которые
необходимы для написания полезных программ, и которые могут
быть легко решаемы операционной семантикой
• Прорыв произошел из теории категорий. Евгенио Моджи
обнаружил, что вычислительные эффекты могут быть
преобразованы в монады
62. Чистая функция
• Является детерминированной
• Не обладает побочными эффектами
• В Haskell все функции чистые, поэтому без монады IO нельзя
написать hello world
74. Apply <*>
• M<(a->b)> -> M<a> -> M<b>
• Аппликативные функторы применяют упакованную функцию к
упакованному же значению:
• Используя apply и return можно сконструировать map
80. Все вместе
• функтор: вы применяете функцию к упакованному значению,
используя fmap или <$>
• аппликативный функтор: вы применяете упакованную функцию к
упакованному значению, используя <*> или liftA
• монада: вы применяете функцию, возвращающую упакованное
значение, к упакованному значению, используя >>= или liftM
81.
82.
83.
84.
85.
86.
87. Either Control Flow
public static ActionResult ViewOrError<TIn, TOut>(
this IQueryController<TIn,TOut> controller, TIn spec)
where TIn : class, new()
where TOut : class
=> spec.ThisOrDefault()
.ToWorkflow(
x => controller.ModelState.IsValid,
x => (OrderFailure?)OrderFailure.ArgumentInvalidState)
.Then(x => controller.Query.Ask(x))
.Then(x => x != null, controller.View, x => OrderFailure.ObjectNotFound)
.Finish(x => x, x => x.ToActionResult());
88. Императивный аналог
protected ActionResult ViewOrErrorImperative<TIn, TOut>(IQuery<TIn, TOut> query, TIn spec)
where TIn : class, new()
where TOut : class
{
if (spec == null)
{
spec = new TIn();
}
if (ModelState.IsValid)
{
var data = query.Ask(spec);
if (data != null)
{
return View(data);
}
return new HttpNotFoundResult();
}
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
96. Сильные стороны
• Повышение надёжности кода
• Возможность формального доказательства корректности программы
• Удобство организации модульного тестирования
• Возможности оптимизации при компиляции
• Возможности параллелизма
• Лучшие возможности композиции и повторного использования кода
• Значительное уменьшение количества строк кода и cyclomatic
complexity
• Лучшая читабельность
97. Недостатки
• Необходимости постоянного выделения и автоматического
освобождения памяти
• Нестрогая модель вычислений приводит к непредсказуемому порядку
вызова функций, что создает проблемы с побочными эффектами
• Отсутствие алгоритмической базы для функциональных структур
данных
• Принципиальная невозможность эффективной реализации некоторых
важных эмпиративных алгоритмов (например, quicksort)
• Нехватка специалистов на рынке труда
• В Haskell ввод/вывод реализован с помощью не тривиальных
абстракций – монад
98. Сколько раз в день ваша бабушка
пользуется ПО?
• Ни одного
• Один раз
• От двух до пяти
• Да постоянно!
99. Сколько раз в день ваша бабушка
пользуется ПО?
• Начисление пенсии
• Оплата товаров в магазине
• Социальные льготы
• Медицинское страхование
• ...
100. Список использованных материалов
• http://fsharpforfunandprofit.com/
• https://habrahabr.ru/post/79713/
• https://habrahabr.ru/post/183150/
• https://habrahabr.ru/post/245797/
• https://ru.wikipedia.org/
• https://ericlippert.com/2013/02/21/monads-part-one/
• http://blog.ploeh.dk/2016/04/11/async-as-surrogate-io/
• https://www.youtube.com/watch?v=ecIWPzGEbFc