Functional Programing

Preview:

Citation preview

λ

ФП появилось раньше ООП и структурного программирования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

Но не получило распространения в индустрии из-за дороговизны памяти на заре computer science, хотя и нашло свое место в научных кругах

Интерес вернулся в последние 5 лет• Закон Мура «выдыхается»• Популярность облачных

решений растет• Big Data, машинное обучение,

боты, когнитивные сервисы

LISP• Появился в 1958 году• Создатель Лиспа Джон Маккарти занимался исследованиями в

области искусственного интеллекта (в дальнейшем ИИ) и созданный им язык по сию пору является одним из основных средств моделирования различных аспектов ИИ• Это один из старейших (наряду с Фортраном и Коболом)

используемых по сей день высокоуровневых языков программирования• Основой архитектуры его является лямбда-исчисление• Интерпретатор Лиспа, написанный на Лиспе, занимает 15 строк

Что не так с состоянием?• Locking, Memory Bandwidth• How To Reproduce / Debug• Race Conditions• Side Effects• Не возможно доказать корректность программы

Чему равен y?var x = 2;DoSomething(x);

// чему равен y?var y = x - 1;

Ответ

-1

Это JavaScript

function DoSomething (foo) { x = false }

var x = 2;DoSomething(x);var y = x - 1;

Функциональное программиирование• Раздел дискретной математики

и парадигма программирования, в которой процесс вычисления трактуется как вычисление значений функций в математическом понимании последних• Понимание функции отличается

от функции как подпрограммы в процедурном программировании

Функция –Соответствие между элементами двух множеств, установленное по такому правилу, что каждому элементу одного множества ставится в соответствие некоторый элемент из другого множества.

Принцип разделения интерфейсаПринцип разделения интерфейсов говорит о том, что слишком «толстые» интерфейсы необходимо разделять на более маленькие и специфические, чтобы клиенты маленьких

public interface IMessyCalc{ float Add(float a, float b); Rocket LaunchRocket(); // WAT?}

Принцип разделения интерфейсаpublic interface ICalc{ float Add(float a, float b);}

public interface IRocketLauncher{ Rocket LaunchRocket(); // ok}

ICalc прощеlet add x y = x + y; // float -> float -> float

Функции и вывод типовlet add1 x = x + 1 // int -> intlet add2 x = x + 2 // int -> intlet add3C = add1 >> add2 //композицияlet add3P x = x |> add1 |> add2 // pipe-операторlet toString x = x.ToString() // 'a -> string

Аналогия с конвеерной лентой

Композиция

Композиция

Каррирование и частичное применениеlet printTwoParameters x y = printfn "x=%i y=%i" x y

//explicitly curried versionlet 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 + ylet add1P = add 1let v = add1P 2

let result = [1..10] |> List.map (fun i -> i+1) |> List.filter (fun i -> i>5)

Изменяемые данные (не тру)let mutable mut = 5mut <- 6

React/Redux Demo

λ-исчисление• Формальная система, разработанная американским математиком

Алонзо Чёрчем, для формализации и анализа понятия вычислимости• Строится на простых двух операциях: аппликации и абстракции• Определены фундаментальные понятия α-эквивалентностью и β-

редукции• Многие функциональные языки можно рассматривать как

"надстройку" над ними

λ-исчисление• Нет циклов• Нет переменных• Нет операции присвоения• Нет изменяемого состояния• Нет условных переходов и циклов• Нет «обычной» рекурсии• Обладает свойством полноты по Тьюрингу

и, следовательно, представляет собой простейший язык программирования

Теорема о неподвижной точке• И в λ-исчислении, и в комбинаторной логике для каждого терма 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/

α-эквивалентность• λx.x и λy.y альфа-эквивалентные лямбда-термы и оба

представляют одну и ту же функцию (функцию тождества). • Термы x и y не альфа-эквивалентны, так как они не находятся в

лямбда абстракции

β-редукция•(λx.2 * x + 1)3 -> 7

Структурирование программExpressions VS StatementsExpression<Func<int, string>> expr = x => { return x.ToString(); };

CS0834 A lambda expression with a statement body cannot be converted to an expression tree

Statementvar str = "test";var i = 1;if (flag){ str += i;}else{ i++;}

Expressionvar fiveOrSix = Math.Cos(1) > 0 ? 5 : 6;

FixedExpression<Func<int, string>> expr = x => x.ToString(); // ok

Что возвращает функция?function DoSomething (foo) { if (foo > 5) return 2; throw "Error";}

var x = 2;DoSomething(x);var y = x - 1;

Exception Handling Considered Harmful•Hidden Control Flow & Corrupt State•Mismatch With Parallel Programming• Exceptional Exceptions• Exceptions only really work reliably when nobody

catches them

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));

Ограничения императивного стиля

Cross-cutting concerns

Convert exceptions into Failures

Алгебраические типы данныхtype Option<'T> = | Some of 'T | None

type Season = Autumn | Winter | Spring | Summer

type Profile = { FirstName: string LastName: string}

Pattern Matchinglet printSomeOrNone x = match x with | Some(y) -> printfn "some: %A" y | None -> printfn "none"

| _ -> printfn "Never hit" // only use if you have to

Организация приложения в функциональном стиле

Монада – это просто моноид в категории эндофункторов• Если не затрагивать тему гомоморфизмов• В чем проблема?

Какие милые котята

Ой, какие щеночки

Уиии, лошадки

Определение порядковых чисел по фон Нейману• Множество S является ординалом тогда и только тогда, когда оно

строго вполне упорядочено отношением «принадлежать» и каждый элемент S одновременно является его подмножеством• Любой ординал есть вполне упорядоченное множество,

состоящее из всех ординалов, меньших его

Основы теории категорий• В программах, использующих операционную семантику, очень трудно

что-то доказать. Чтобы показать некое свойство программы вы, по сути, должны «запустить ее» через идеализированный интерпретатор• Но есть и альтернатива. Она называется денотационной семантикой и

основана на математике. В денотационной семантике для каждой языковой конструкции описана математичесая интерпретация• По сравнению с теоремами, которые доказывают профессиональные

математики, задачи, с которыми мы сталкиваемся в программировании, как правило, довольно просты, если не тривиальны

Какова математическая модель для чтения символа с клавиатуры, или отправки пакета по сети?• Долгое время это был бы неловкий вопрос, ведущий к довольно

запутанным объяснениям. Казалось, денотационная семантика не подходит для значительного числа важных задач, которые необходимы для написания полезных программ, и которые могут быть легко решаемы операционной семантикой• Прорыв произошел из теории категорий. Евгенио Моджи

обнаружил, что вычислительные эффекты могут быть преобразованы в монады

Чистая функция• Является детерминированной• Не обладает побочными эффектами• В Haskell все функции чистые, поэтому без монады IO нельзя

написать hello world

Категория

Warning Math ahead

Моноид – это категория• 1 + 2 = 3• 1 + (2 + 3) = (1 + 2) + 3• 1 + 0 = 1• 0 + 1 = 1

Строки – тоже моноид• "Foo" + "Bar" = "FooBar"• "Foo" + ("Bar" + "Baz") = ("Foo" + "Bar") + "Baz"• "Foo" + "" = "Foo«• "" + "Foo" = "Foo"

Монада как значение в контексте

Монада как значение в контексте

Map (fmap, lift, Select, <$>, <!>)1. (a->b) -> M<a> -> M<b>2. fmap id = id3. fmap (g >> f) = (fmap g) >> (fmap f)

Return ( pure, unit, yield, point)• A -> M<A>

Apply <*>• M<(a->b)> -> M<a> -> M<b>• Аппликативные функторы применяют упакованную функцию к

упакованному же значению:• Используя apply и return можно сконструировать map

Bind (flatMap, andThen, SelectMany >>=)• (a -> M<b>) -> M<a> -> M<b>

>>=

Maybe• Является функтором, аппликативным функтором и монадой

одновременно

Все вместе• функтор: вы применяете функцию к упакованному значению,

используя fmap или <$>• аппликативный функтор: вы применяете упакованную функцию к

упакованному значению, используя <*> или liftA• монада: вы применяете функцию, возвращающую упакованное

значение, к упакованному значению, используя >>= или liftM

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());

Императивный аналог 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); }

Монады• LINQ• Async/Await• Map/Reduce• Lazy<T>• Nullable<T>• IEnumerable<T>• Task<T>

SelectMany public static Maybe<int> Add(this Maybe<int> m, Maybe<int> add) => from v1 in m from v2 in add select v1 + v2;

Бастион ООП содержит монады (на странице 243)

Interpreter is Free Monad

Interpreter• Expression Tree• RegEx

Другие элементы ФП, которые мы используем каждый день• Делегаты• Ad-Hoc полиморфизм• String• Замыкания• Bind• Promise

ООП VS ФП• Инкапсуляция -> Immutability• Наследование -> Композиция, Каррирование, Частичное

применение, Continuation Style• Полиморфизм -> Вывод типов, Монады

Сильные стороны• Повышение надёжности кода• Возможность формального доказательства корректности программы• Удобство организации модульного тестирования• Возможности оптимизации при компиляции• Возможности параллелизма• Лучшие возможности композиции и повторного использования кода• Значительное уменьшение количества строк кода и cyclomatic complexity• Лучшая читабельность

Недостатки• Необходимости постоянного выделения и автоматического

освобождения памяти• Нестрогая модель вычислений приводит к непредсказуемому порядку

вызова функций, что создает проблемы с побочными эффектами• Отсутствие алгоритмической базы для функциональных структур данных• Принципиальная невозможность эффективной реализации некоторых

важных эмпиративных алгоритмов (например, quicksort)• Нехватка специалистов на рынке труда• В Haskell ввод/вывод реализован с помощью не тривиальных

абстракций – монад

Сколько раз в день ваша бабушка пользуется ПО?• Ни одного• Один раз• От двух до пяти• Да постоянно!

Сколько раз в день ваша бабушка пользуется ПО?• Начисление пенсии• Оплата товаров в магазине• Социальные льготы• Медицинское страхование• ...

Список использованных материалов• 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