Пояснительная бригада: мем про побочные эффекты
На картинке написано:
— Я прогаю на Haskell
— Это что значит?
— Это значит, что он боится побочных эффектов!
— Ничего подобного!
— printf («Hello world!"\n);
— Патрик, хорош, ты его перепугал!
Поясняет Паша Вавилин, наставник на курсе по Python.
В функциональном программировании есть понятие side-эффекта: это когда функция не только возвращает результат, но и делает что-то кроме. Например, вывести что-то на экран, записать в файл, поменять что-то внутри какой-то другой переменной в программе — это всё побочные эффекты (side effects).
Программисты на функциональных языках программирования (например, Haskell) придумывают множество сложных идей (всякие монады), чтобы не иметь сайд-эффектов, как будто они боятся их в реальности.
Дополнительно пара слов от «Кода»: мы как-то давно писали про чистые функции. Идея в том, что чистой считается та функция, которая получила что-то на вход, обработала, выдала на выходе. И мы должны быть уверены, что все эти вычисления произошли только внутри функции и ничего другого в программе не было затронуто.
Например, у нас есть переменная в корне программы. Мы её используем, чтобы считать какие-то нужные нам события. Если какая-то функция в программе добавляет что-то в эту переменную, это побочный эффект.
Вот мы программируем себе и положили побочный эффект «Увеличить глобальный счётчик» в одну из функций. Вызвали её по делу. Она правильно всё посчитала и увеличила счётчик. Пока что ничего страшного.
Через полгода другой программист поддерживает за нас программу. Видит: «О, эта функция делает то, что мне нужно, вызову её». И он не знает, что, помимо своей основной работы, она ещё увеличивает глобальный счётчик (забыл про побочный эффект). Он её вызывает в цикле тысячу раз, и она тысячу раз увеличивает счётчик. А счётчик, в свою очередь, завязан на что-то другое в программе. И в итоге вся программа ломается.
Чтобы такого не было, разработчики стремятся писать чистые функции. А в функциональных языках и подавно.
Tabnine напишет код за вас
Данные для обучения модели Tabnine собраны с открытых репозиториев на GitHub.
Сервис работает для Python, Java, Haskell и C++. Tabnine совместим с VS Code, Sublime Text, Atom, Vim и другими редакторами кода.
Больше полезных ии сервисов в моем тг (ссылка в описании профиля), там я рассказываю, как использовать нейросети для бизнеса, работы и учебы
Примеры функционального программирование для новичков
Представь, что у тебя есть три волшебные машины: машина A, машина B и машина C. Каждая машина делает что-то особенное с предметами:
1. Машина A окрашивает вещи в зеленый цвет.
2. Машина B делает вещи в два раза больше.
3. Машина C превращает фрукты в овощи.
Теперь, используя язык программирования Haskell, мы можем создать код, который моделирует это поведение:
haskell
type Item = String
machineA :: Item -> Item
machineA item = "зеленый " ++ item
machineB :: Item -> Item
machineB item = "большой " ++ item
machineC :: Item -> Item
machineC "яблоко" = "маленькая красная морковка"
machineC item = item
transformItem :: (Item -> Item) -> (Item -> Item) -> (Item -> Item) -> Item -> Item
transformItem = (.) . (.)
В этом примере мы определяем тип Item для представления предметов. Мы также определяем функции machineA, machineB и machineC, которые соответствуют машинам A, B и C из нашего описания. Затем мы определяем функцию transformItem, которая позволяет нам применить три машины одна за другой к предмету.
haskell
main :: IO ()
main = do
let item = "яблоко"
let transformedItem = transformItem machineA machineB machineC item
putStrLn transformedItem
Здесь мы определяем item как "яблоко" и применяем функцию transformItem к нему, чтобы получить преобразованный предмет. Затем мы выводим результат на экран. В итоге, мы получим "большая зеленая морковка", как и ожидалось из нашего описания.
Таким образом, с помощью функционального программирования на языке Haskell, мы смогли элегантно и кратко описать сложную операцию, используя мощные функции композиции и стиль без указания точки.
Больше примеров в https://t.me/Koding404
Занятия с напарниками по Haskell (4)
Добрый день. Продолжаю делиться наблюдениями из нашей группы по изучению Haskell (
часть 1, часть 2, часть 3 ). Воскресенье, дел мало. Можно посидеть и посочинять что-то, или рассказать. Тем более давно была предыдущая часть.
(Оффтоп) Прикольно. Только что узнал, что абзацы в тексте в редакторе пикабу можно разделять не только через "enter", но и через специальную кнопку "текст". Подобные блоки текста можно перетаскивать в режиме drag-and-drop.
Занятия у нас продолжаются. Был небольшой перерыв в 2 недели. Один из участников был в отпуске. Он хорошо отдохнул. Природа, свежий воздух, все дела. Вернулся, и все стало по прежнему, но хотя бы не так душно (шутка). 4 занятия в неделю. Читаем книгу, общаемся, решаем задачки.
И начитали мы уже 300 страниц. Из 1200. Достижение =). Да, пусть еще не монады. Но и не сказать , что идем на расслабоне. Книжка хитрая. Идеально держит баланс между "легко заскучать" и "трудно, что легче забросить". За это ей большой респект. Ради интереса попробовал почитать знаменитую "Изучай хаскель во имя добра" - ну не то. Даже несмотря на перевод, картинки, красивое оформление - путь изучения идет через спотыкания. Но это лично мое мнение. Не надо делать на этом глобальные выводы =)
Проходили недавно рекурсию. Ох и злобная тема. Вроде логика простая. Задал базовое условие, и вызываешь фукнцию внутри определения другой. Но как пошли задачки с "переподвыпертами" - так сразу надо думать. Дополнительные вспомогательные функции писать, совмещать их по типам. Зато сразу научился различать одинарные и двойные кавычки. В одинарных кавычках тип данных - Char, в двойных - [Char], то есть String. Просто на этом моменте чаще всего не сходились типы в задачках на рекурсию.
Сейчас изучаем списки. Забавно автор их подает. Через такие термины как "cons cell" и "spine". Знакомит с основами ленивых вычислений haskell. Например, length списка будет вычислять только хребет списка (spine), не заботясь о том, что лежит в ячейках (cons cell) самого списка. Не заботясь - значит не вычисляя выражение, оставляя его в виде thunk. А само выражение, допускающее внутри себя thunk-и, находится в weak head normal form
( https://wiki.haskell.org/Weak_head_normal_form ). Читатель скажет: "фигня какая-то". А это только введение =)
Ладно уж. Учимся и страдаем. Страдаем и учимся. Хорошо, что все-таки вместе, в группе. Потому что в одиночку неприязнь к подобным вещам наступает быстро. А так, не хочется сильно тупить на созвоне - так даже иногда заранее читаешь наперед.
Прикол даже появился. Как-то попалась нам задачка вычислить 10-милионный палиндром на целых числах. Не спрашивайте, откуда задачка взялась. Кто-то пришел, ноги с улицы не вытер, и занес нам это. Так эта задачка сильно раззадорила участника нашей группы. И чтобы её решить - он походу наперед весь хаскель уже выучил. И оптимизации, и массивы, и фиг еще знает что. А я что - а я дождусь, когда он её решит, и я поинтересуюсь решением. Да, такой вот я нахлебник в этом вопросе =)
Автор упомянул функторы вскользь. Ах. Прям запахло приближением монад. Пока не совсем понятно. Абстракция над преобразованием абстрактного структурированного типа. Знакомиться подробно будем позже. Но радует, что я все ближе и ближе к своей цели по пониманию монад.
Вот такие дела. Пора выводы делать. А они простые. Хорошо, что с пользой трачу время своего сна на haskell, хоть это и не всегда приятно по ощущениям. Радует, что что-то остается в голове. Стремлюсь к большему. Чего и вам, читатели, желаю =)
Занятия с напарниками по Haskell (3)
Добрый вечер. Продолжаю делиться наблюдениями из нашей группы по изучению Haskell (
часть 1, часть 2 ). Скорее всего, пишу текст для себя, попутно повторяя \ вспоминая пройденный материал. Итак, поехали.
Прошли базовые функции для списков: head, tail, take, drop, !! . Отдельная благодарность автору книги в этом моменте, что он не стал подробно расписывать, какие же крутые это вещи, а просто обозначил их как API. Берите, пользуйтесь. Вы же не глупые: сможете различить, что значит взять голову списка, а что значит взять первые 5 элементов. Так что эту тему проскочили быстро.
А дальше начались базовые определения даже не столько языка Haskell, а программирования в целом. Type, scope, local bindings, data structure, list... Хорошо, что у каждого из нас есть какой-никакой программистский бэкграунд. Поэтому именно эти определения у нас не вызвали затруднения. А вот следующие термины уже нас напрягли.
Data declaration (0), type constructor (1), data constructor (2). Вот в наших головах слово "конструктор" связано с тем, что это функция, которая должна что-то создать. Конструктор может принимать аргументы. А когда мы пишем (0) data (1) Bool = (2) False | (2) True - тут вроде как ничего не создается в прошлом, привычном для нас понимании. Ну ок, допустим, что мы ввели новую сущность тип Bool. Да, он появился на свет, типа создался. Но False, True - почему их называем конструкторами? Это же значения, это данные, которые "населяют" наш Bool. Их не вызывают. Бесспорно , какие-то догадки есть. Кто-то наперед заглянул, кто-то гугл помучил. Но все равно очень непривычно. В этом моменте впервые столкнулись с тем, что наш предыдущий опыт скорее даже мешал, чем помогал в осознании текста книги.
Затем автор ввел понятие переменной типа (type variable). Заставил нас различать оператор ++ и функцию concat. Заставил прочувствовать разницу следующих сигнатур: 1) [a]->[a]->[a] и 2) [[a]] -> [a]. Признаюсь, вроде бы такой простой пример, но у меня с ним трудности. До сих пор спотыкаюсь на различии этих видов конкатенации. И там, и тут возвращаются списки. А как они получились - мне не важно. Может, как станет важно, так и все станет легко и прозрачно.
Дальше нас начали знакомить с базовыми типами. Научились отличать смысл следующих слов: Integral, Int, Integer. Через пару страниц посмеялись, что, оказывается, в комплект входят Int8, Int16... Все познается в сравнении. JS, твой универсальный double на все времена по сравнению с зоопарком в Haskell кажется таким простым. А Integer от Haskell удивительно похож по своей задумке на BigInt в JS. Вот интересно, а как можно считать бесконечно большие числа на компьютере, если не через биты памяти того же стандарта IEE-754? Переводить в строки, строить отдельный парсер... Кто знает, напиши в комментариях, в чем там фокус?
В итоге, мы научились чувствовать, что в Haskell есть отдельный уровень типизации, а есть отдельный уровень вычислений. Путать их нехорошо. Их надо держать под своим контролем в связке и согласованности. Еще попутно автор намекал, что нас ждут Type classes. Просил не расстраиваться. Сказал, что пока думайте о них как об интерфейсах в классических языках программирования про ООП. А потом, когда дойдем до этой темы, там все нам объяснит. Брр. Опять отсылка в будущее. Бесит.
А пока мы остановились на упражнениях по пройденному материалу: базовые операции над числами, строками, списками. Думается, что раз мы честно проходили материал и не позволяли халявить друг другу, то проблем с решением этих упражнений у нас не будет. Ну а если будут... - предлагайте ваши варианты =)
Занятия с напарниками по Haskell (2)
Здравствуйте. Продолжаю описывать интересные моменты из наших занятий в группе по Haskell ( Предыдущий пост ).
Прошла еще неделя. Мы стабильно выдерживаем 1ч 20мин за занятие в день. Иногда чуть подольше.
Книжка тупиков пока нам не ставит. Но после первой главы восторженное впечатление от книги подупало. Это связано скорее всего с 2 вещами. Первое, это неравномерность подачи материала в плане трудности. То есть пошли места, где все очень легко. Ты расслабляешься, читаешь в быстром темпе страницу, другую, а потом раз - и буксуешь абзац за абзацем.
Второе - о, это особенный момент, который встречается много в каких обучающих материалах!
Этот неприятный момент - прыжки в будущее. Можно читать, разбирать базовые операции со строками, но уже в примерах используются модули и\или монада IO. Причем автор книги уверяет читателя в том, что вы узнаете и поймете эти моменты потом. Сейчас же просто скопируйте пример кода и запустите. Сместите фокус на то, что делает функция putStrLn, а на тип не смотрите. Потом узнаете зачем в типе у этой функции написано IO.
Знаете, вот когда постигал основы JS в свое время, часто натыкался на подобное. Вот внешняя функция возвращает внутреннюю функцию, а переменные внешней будут жить и использоваться внутренней благодаря замыканию. Да вы это обязательно поймете. Будете писать код, примеры, упражнения, и все встанет на свои места. А пока изучим следующую тему.
Теряется последовательность изложения материала. А это сбивает. И если мозг можно усмирить - реально сместить фокус на важное, то на эмоциональном уровне остаются мелкие "зазубрины". Эх, вот бы еще и то знать. А как вон ту штуку использовать, про которую рассказали? Блин, ну интересно же было бы понять. А прыгать вперед еще хуже. Это и с мысли собьет. И все равно это будет непонятно.
В общем, как всегда, не бывает ничего идеального. Мы даже уже даже начали смеяться с подобных моментов в книге. Шутили, типа ну вот, раз уже здесь про монады начали упоминать, значит на следующей неделе книжку закончим.
В начале занятий иногда проводим опросы, вдруг кто что подзабыл. Был случай: дали напарнику пример из книги на подстановки из лямбда-исчисления, применение термов. И пример был решен. Вроде даже хорошо. Только забыли про альфа-эквивалентность. И следовательно, ответ был не верен. Ничего страшного. Другой пример уже был сделан как надо.
Начали делать первые практические упражнения. Тут наши предпочтения разошлись. Моя личная цель - это изучения языка, а не его экосистемы. Поэтому я просто открываю вкладку с онлайн компилятором Haskell и тут же решаю задачи. Ребята же захотели установить среду разработки себе на компьютер локально. GhcUP или как-то так. Там еще stack, cabal. Брр, мне пока лень в этом разбираться. Наверное, напарники поступают лучше. Может, в будущем они меня мотивируют поступить так же.
Ну и поделюсь еще одним интересным случаем. Разбирали мы простейшие математические операторы в Haskell. Умножение, сложение, вычитание. И div, mod, quot, rem. Какой остаток будет от деления 20 на 6? Два - бодро отвечает вся группа. А каков будет остаток от деления 20 на -6? И все, мы поплыли. Думали, сейчас в гугле посмотрим, за 5 минут разберемся. 3 парням, которым за 30, и не справиться с таким простейшим вопросом? Ага. Не справились, ни за 5 минут, ни за оставшиеся полчаса того занятия. Справились за выходные к следующему уроку. И то, на троих. Первый разобрался, что такое делимое, делитель, неполное частное, остаток от деления. Второй указал на момент, что бывает округление в минус бесконечность и к нулю. Третий собрал эти факты в кучу, и наконец-то - вы можете нас поздравить с пониманием куска школьной программы.
Посмотрим, что будет дальше. Будут новые главы книги, будут новые вызовы. Справимся? =)