haskell комментарии в коде

Выбираем и возвращаемся

В этой главе мы встретимся с условными конструкциями, выглянем в терминал, а также узнаем, почему из Haskell-функций не возвращаются (впрочем, последнее — не более чем игра слов).

Выглянем во внешний мир

Стандартная функция putStrLn выводит строку на консоль. А если говорить строже, функция putStrLn применяется к значению типа String и делает так, чтобы мы увидели это значение в нашем терминале.

Да, я уже слышу вопрос внимательного читателя. Как же так, спросите вы, разве мы не говорили о чистых функциях в прошлой главе, неспособных взаимодействовать с внешним миром? Придётся признаться: функция putStrLn относится к особым функциям, которые могут-таки вылезти во внешний мир. Но об этом в следующих главах. Это прелюбопытнейшая тема, поверьте мне!

И ещё нам следует познакомиться с Haskell-комментариями, они нам понадобятся:

На всякий случай напоминаю команду сборки, запускаемую из корня проекта:

После сборки запускаем:

Выбор и выход

Выбирать внутри функции приходится очень часто. Существует несколько способов задания условной конструкции. Вот базовый вариант:

А кстати, что значит «возвращаем»? Ведь, как мы узнали, функции в Haskell не вызывают (англ. call), а значит, из них и не возвращаются (англ. return). И это действительно так. Если напишем:

при запуске увидим это:

Круглые скобки включают выражение типа String по схеме:

произошла бы ошибка компиляции, и это вполне ожидаемо: функция putStrLn применяется к одному аргументу, а тут их получается два:

Не знаю как вы, а я не очень люблю круглые скобки, при всём уважении к Lisp-программистам. К счастью, в Haskell существует способ уменьшить число скобок. Об этом способе — в одной из последующих глав.

Так что же с возвращением из функции? Вспомним о равенстве в определении:

То, что слева от знака равенства, равно тому, что справа. А раз так, эти два кода эквивалентны:

есть ни что иное, как:

Для любопытных

Внимательный читатель несомненно заметил необычное объявление главной функции нашего проекта, функции main :

Источник

Haskell — ленивый язык программирования

Он сам решает, что и когда нужно посчитать.

Haskell — необычный язык с точки зрения тех, кто привык к JavaScript, С++, Python или любому другому императивному языку. Всё дело в том, что Haskell — функциональный язык. Мы уже рассказывали, чем отличаются функциональные языки от остальных, теперь посмотрим на них в деле.

👉 Haskell — не совсем для обычных вещей, и стандартное приложение с красивым интерфейсом на нём сделать не получится. А вот сделать серверную часть, которая возьмёт на себя все сложные вычисления, или консольную программу, которая вызывается через командную строку, — вполне. Лучше всего Haskell справляется с точными вычислениями и расчётами, поэтому чем лучше вы будете знать математику — тем лучше для кода.

Синтаксис в Haskell

Самая простая программа на Haskell выглядит так:

Теперь мы явно указали тип функции main — это ввод и вывод каких-то результатов (IO — input/output, ввод-вывод). Ещё в Haskell есть такие базовые типы данных:

Математические операции в Haskell работают и выглядят как обычно: +, –, *, /.

Функции и их значения

Почти всё в Haskell делается через функции. Задача программиста — описать функцию таким образом, чтобы компилятор понял:

Получается, что функции — это наборы правил, по которым обрабатываются данные. Например, если нам нужно найти факториал любого числа, то на Haskell это можно записать так:

Вторая строка отвечает за то, что если нужно посчитать факториал ноля, то это будет единица.

Если пока непонятно, что тут происходит, — почитайте про рекурсию, там мы подробно разобрали этот пример.

Мы только что записали строгое математическое определение на Haskell и объяснили функции, как ей найти факториал любого числа. Но если мы попробуем вызвать факториал числа 4.5, то Haskell выдаст ошибку, потому что 4.5 не относится к типу Integer.

👉 Вся программа на Haskell — это набор таких функций и правил, которые могут быть связаны друг с другом. Все данные обрабатываются строго по этим правилам, и на выходе получается точный результат. Можно работать с любыми данными, если перевести их в понятный для языка вид, поэтому Haskell часто используют в тех областях, где нужна надёжность и гарантированная точность вычислений.

Ленивые вычисления

Haskell — язык, который поддерживает ленивые вычисления. Это значит, что он вычислит нужные значения в любой функции не тогда, когда программист это написал, а когда это значение действительно понадобится в программе.

Например, у нас есть функция, которая возвращает какое-то значение после вызова. Если это значение прямо сейчас не нужно или оно не используется при вызове функции, то Haskell не будет его считать. Он дождётся того момента, когда значение функции понадобится в другом месте, и только тогда посчитает его.

Ленивые вычисления помогают сократить нагрузку на ресурсы и делают программы быстрее и эффективнее. Если вы пишете калькулятор, в котором предусматриваете все математические действия, но пользуетесь только сложением, то Haskell даже не будет обращать внимания на остальное. Он будет знать, что у вас есть код, который, если что, может ещё умножать и делить, но делать с ним пока ничего не будет.

Для чего нужен Haskell

Обработка текста и синтаксический анализ. В Haskell легко заложить правила любого языка, по которым строятся языковые конструкции, и научить его анализировать этот язык. Например, он может делить предложения на слова и предлоги, находить связи между ними, смотреть, всё ли написано без ошибок, или находить неправильные языковые конструкции. Это работает и для обычных языков (русский или английский), и для языков программирования.

Компиляторы. Благодаря тому, что Haskell всё делает строго по правилам, это отличный инструмент для написания компиляторов. Задача любого компилятора — взять текст программы, проверить, нет ли в нём ошибок, и выполнить код. Так как Haskell отлично работает с текстом и при этом не ошибается в правилах, то он может преобразовывать команды другого языка в команды для компьютера.

Финансовые инструменты. Основное требования к финансовым инструментам — гарантированная точность вычислений и отсутствие ошибок, поэтому Haskell любят использовать корпорации для своих внутренних инструментов. Это могут быть системы учёта банковских транзакций, биржевой торговли, анализа рисков или инструменты финансового мониторинга между отделами.

Промышленные приложения. Haskell позволяет задавать нужные правила и обрабатывать данные по этим правилам — именно это нужно предприятиям, чтобы построить системы поддержки принятия решений или внутреннего аудита. Это снимает нагрузку с людей и позволяет алгоритмам эффективнее находить слабые места в компании или точки для промышленного роста.

Источник

Говорят, Haskell — язык для гениев и академиков. Правда?

haskell комментарии в коде. o6qs0dg9mgydyepqfcn6kpobfnc. haskell комментарии в коде фото. haskell комментарии в коде-o6qs0dg9mgydyepqfcn6kpobfnc. картинка haskell комментарии в коде. картинка o6qs0dg9mgydyepqfcn6kpobfnc. В этой главе мы встретимся с условными конструкциями, выглянем в терминал, а также узнаем, почему из Haskell-функций не возвращаются (впрочем, последнее — не более чем игра слов).

Однажды я разговаривал с основателем израильского стартапа, который разрабатывал скоростную базу данных на GPU. В их стеке были Haskell и C++, и основатель жаловался, как тяжело найти людей в команду. В Москву он прилетал в том числе искать хороших программистов.

Я осторожно спросил, не лучше ли было использовать что-то более распространенное и новое. И хоть ответ был вежливым и конструктивным, между строк мне показалось: «Пфф, даже не упоминай эти игрушечные япы».

Все, что я слышал про Хаскель со стороны с тех пор, сводилось к одному — «с ним шутки плохи». Чтобы узнать хаскелистов получше, я пришел с расспросами к ним в телеграм-чат. Было довольно страшно, и как оказалось, не зря.

О Хаскеле не стремятся говорить популярно, и на такие затеи, кажется, поглядывают с презрением. Уж если говорить — то с максимальной полнотой и объективностью. «Одно из характерных качеств Хаскеля как языка и сообщества в том, что они вместе не стремились стать популярными, дав простой ответ на популярные вопросы. Вместо этого выстраивали логичный principled путь решения реальных проблем, а не быстрого проникновения в сердце прохожего интересующегося» — написали мне там.

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

Денис Мирзоев (nolane): В университете по предмету «Языки программирования» мне предложили пройти курс на Coursera по Хаскелю за один дополнительный балл из ста. Потом ещё был курс функционального программирования, на котором проходили Хаскель. Написал курсовую и выпускную работу бакалавра по GHC. Нашёл работу в качестве Хаскель-программиста.

Было тяжело, и тяжело до сих пор. Когда начинаешь изучать Хаскель, приходится постигать очень много новых концепций. Это сложная работа. Буквально учишься программировать заново.

Многим сейчас будет трудно вспомнить, как они начинали свой путь в программировании, как трудно было понять, что такое «указатель», что такое «функция», что такое «класс». Возможно поэтому изучение Хаскеля даётся так тяжело. С возрастом становится труднее усваивать новое.

Doctor_Ryner: Однажды на испытательном сроке я завалился на Redux, поэтому, смотря уроки от его создателя, решил поближе с этим всем познакомится. Сначала применял изученные практики в JavaScript, но потом узнал о Haskell, который считается истинным функциональным языком. Меня сразу привлекла его элегантность и куча новых неизвестных мне концепций.

Приходилось нелегко с бесконечными туториалами про монады на примере бурито, которые сильно путают. Также императивный бэкграунд сильно мешает открыться новым концепциям.

Юрий Сыровецкий (cblp): Сложнее всего изучать Хаскель вторым, когда не прошёл синдром утёнка к первому языку.

Чем хорош и чем плох язык?

Doctor_Ryner: Язык очень краткий, элегантный и гибкий, не зря на нем половина библиотек — это EDSL (как минимум такое впечатление).

Юрий Сыровецкий (cblp): Высокая выразительность, легко переносить предметную область в код, оптимальное сочетание императивной и функциональной парадигм. Легко строить абстракции над данными и алгоритмами, что позволяет думать о задаче, не отвлекаясь на несвязанные мелочи.

Джон Доу: Строгая сильная (я бы сказал, фашистская) типизация.

Игорь Шевнин (interphx): Очень выразительная система типов. Не настолько мощная, как у Idris или Agda, но достигает той золотой середины, когда выразить можно почти всё, и при этом вывод типов работает нормально. Не надо везде помечать их вручную.

Но мощная система типов заставляет внимательнее относиться к передаваемым значениям. Куча определений типов может выглядеть как бойлерплейт. Каждая команда использует свой набор расширений или не использует их вообще. Код более «плотный» — одна строчка часто несёт больше информации, чем в других языках, поэтому неопытному разработчику сложнее его читать.

Doctor_Ryner: Изучая Haskell, ты скорее всего наткнешься на высказывание «if it compiles, it is probably correct». Тут нет null, сама функциональная парадигма очень строга и принуждает тебя следовать определенным правилам, которые в большинстве случаев ведут к лучшему дизайну.

Например, в языке нет переменных — только константы. Тебе не приходится следить за тем, что и где ты присваиваешь. Haskell поощряет использование чистых функций, что ведет к отсутствию побочных эффектов. Функциональный дизайн просто заставляет программу работать как одно целое, в противовес ООП, где в мир выброшена куча объектов и объекты пытаются общаться друг с другом путем побочных эффектов, превращая приложение в непредсказуемое месиво. На работе достаточно страдаем от такого с C# в Unity.

Денис Мирзоев: Встроенная ленивость повышает выразительность языка. Многие алгоритмы становятся проще. Она может увеличить производительность, если результаты промежуточных вычислений не будут использоваться. (К примеру `head. sort` работает за линейное время).

Игорь Шевнин: Ленивая модель вычислений обычно помогает, но когда порядок вызова функций важен, может быть трудно разобраться, что происходит.

Doctor_Ryner: Он компилируемый, что сразу же дает огромный прирост в скорости.

Денис Мирзоев: По скорости сравним с Java, но не такой быстрый как C.

Игорь Шевнин: Из коробки есть поддержка расширений, которые позволяют допилить язык и систему типов. При этом есть много широко используемых расширений, которые знакомы сообществу, имеют приличные примеры и документацию и не являются нишевыми.

Doctor_Ryner: В стандартной библиотеке Prelude есть очень плохие функции наподобие read, head, readFile, которые могут выкинуть Exception и положить программу, вместо того, чтобы возвращать Maybe. Поэтому приходится использовать альтернативы или писать свои реализации.

Игорь Шевнин: Главная проблема — отсутствие стандартизации, вплоть до того, что многие заменяют стандартную библиотеку на одну из несовместимых между собой альтернатив. Есть разногласия в сообществе на тему того, какой должна быть стандартная библиотека, что должно входить в ядро языка, а что допиливаться расширениями, и по моим ощущениям это тормозит развитие языка.

Денис Мирзоев: Не хватает тулов: нет полноценной IDE, очень мало средств для замеров производительности, нет отладки «по шагам» — это вообще говоря фундаментальная проблема.

Для каких проектов Haskell подходит лучше всего?

Юрий Сыровецкий: Для сложных задач, связанных с безопасностью или деньгами, где высока цена ошибки.

Doctor_Ryner: Для всего, где нужно проводить вычисления, преобразование и анализ данных. Сильно удивлен, что Haskell менее популярен в Data Science, чем Python.

Игорь Шевнин: Я бы не рискнул использовать его для встраиваемых систем (производительность неплоха, но всё же есть значительный оверхэд по потреблению памяти из-за ленивых вычислений) и мелких скриптов (там эта строгость просто не нужна). Также надо понимать, что найти разработчиков в команду намного сложнее, чем для мейнстримных языков.

Джон Доу: Чтобы писать промышленный код, который будут читать другие — вам нужна будет целая команда хаскеллистов. Такую мало кому удалось собрать.

Игорь Шевнин: Но благодаря краткости и строгости Haskell подходит почти для любых задач.

Начинать изучать разработку с Haskell — хорошая идея?

Игорь Шевнин: Начинать — вряд ли, потому что подавляющее большинство кодовых баз, с которыми человеку придётся работать, написаны не на нём.

Джон Доу: Плохая, плохая идея! Языки не из семейства ML — а такие из промышленных вообще все — потом будут для вас шоком.

Денис Мирзоев: Обычно люди сначала изучают математику, потом переходят к программированию. Поэтому изучение языка, использующего математические концепции (алгебраические типы данных, чистые функции), должно быть проще императивных. То есть я считаю, что это хорошая идея.

Doctor_Ryner: Всех новичков, которых я обучаю, я обязательно знакомлю с Haskell. Люди, которые не изучали императивные стиль, гораздо проще ориентируются в функциональном коде и быстрее учатся, потом даже если работают с объектно-ориентированными языками, они привносят хорошие архитектурные решения и функциональные практики.

Юрий Сыровецкий: Начинать лучше сразу с нескольких принципиально разных языков, например, Си, Хаскель и Смолток, в любом порядке. Ни один язык в
отдельности не даст полного понимания.

Haskell — достаточно старый язык. Это хорошо или плохо?

Юрий Сыровецкий: Развивается язык очень активно, груз совместимости только ради совместимости не тянет.

Джон Доу: Стандарт принят в 1998-ом, но это не заметно: до сих пор новые версии компилятора, потенциально ломающие обратную совместимость, выходят примерно раз в полгода.

Денис Мирзоев: Haskell не старый, а проверенный временем. В язык никогда не попадут непродуманные изменения. Так что это скорее хорошо.

Про Haskell говорят, что это один самых сложных языков. Это так?

Doctor_Ryner: Как сам язык — нет. Сложны скорее абстракции, которые в нем используются. Человек, который никогда не видел кода на Haskell, может просто сойти с ума от потока новой информации и разных непривычных конструкций.

Масло в огонь подливает то, что язык накладывает кучу «ограничений», не позволяет или сильно затрудняет кучу вещей, которые не вписываются в функциональную концепцию.

Джон Доу: Чтобы первый элементарный проект хотя бы просто скомпилировался, ушло почти два месяца курения учебников, мануалов и туториалов по вечерам. Правда скомпилировавшись, проект сразу заработал и фигачил под полной нагрузкой (6k rps с пиками до 15) полгода, без изменений вообще.

Денис Мирзоев: Готов поспорить, что если школьник начнёт изучать программирование с Хаскеля и продвинется достаточно далеко, то императивное программирование ему покажется более сложным и менее интуитивным.

Игорь Шевнин: Сложность относительна. Из мейнстримных языков я всё ещё считаю C++ самым сложным. Языки для доказательства теорем (Agda, Coq) будут посложнее Хаскелля в концептуальном смысле. Haskell — несложный язык, но его паттерны и библиотеки — стандартные и сторонние — выучить удастся далеко не сразу.

Всегда ли его сложность оправдана?

Игорь Шевнин: Паттерны и высокий уровень абстракции оправданы, так как делают код надёжнее и короче. Но думаю, что операторы, имена функций и многие другие вещи могли бы быть понятнее.

Doctor_Ryner: Зачастую сложные конструкции Haskell позволяют создавать очень короткие решения, которые еще и оказываются очень гибкими и модульными.

Юрий Сыровецкий: Разве что управление эффектами бывает громоздко, хотя почти
всегда это лучше, чем отсутствие управления. Но над его упрощением
ведутся работы.

Джон Доу: Язык для привыкших к обычным python/php/whatever вообще производит впечатление ортогонального к реальности. Для людей, которые изначально не интересовались теорией категорий, добиться результатов с абсолютного нуля — очень тяжело.

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

Haskell видится языком как будто для математиков, а не для разработчиков. Как думаете, он не распространен широко из-за этого?

Денис Мирзоев: Это демонстрация принципа, которому следуют главные разработчики Хаскеля — «avoid success at all costs». Смысл, конечно, не в том, что нужно избегать успеха, а в том, что нужно избегать успеха, цена которого слишком высока.

Можно было сделать Хаскель популярным. Есть, к примеру, поддержка этого языка со стороны Microsoft. Можно было сделать язык более императивным, принимать какие-то быстрые и непродуманные решения, чтобы завоевать популярность. Можно было использовать много грязных приёмов, но благодаря такой правильной позиции главных разработчиков ничего подобного не было.

Да, популярность языка не очень высока, но зато не страдает его качество. Мне очевидны преимущества Хаскеля по отношению к императивным языкам, большинство его проблем разрешимы, поэтому я уверен, что большая популярность ещё придёт по мере его развития.

Юрий Сыровецкий: Таким он видится только людям, ничего про него не знающим. В
«реальной» разработке Хаскель применяется давно, примеры легко найти в
вашей любимой поисковой системе. В частности, мы в ЛК использованием
Хаскеля довольны, и ничего другого на его месте не видим.

Игорь Шевнин: Что такое «язык для математиков» я толком не знаю. Это либо R/MatLab/Mathematica для расчётов и статистики, либо Python, потому что он простой и требует меньше инженерного бэкграунда. Но не Haskell. Алгебраические понятия типа моноидов используются в нём из практических соображений, а не только для дополнительной строгости.

Основную роль в популярности сыграла историческая распространённость C/C++/Java/C# в энтерпрайзе, они заняли нишу. Но сейчас многие компании начинают использовать Haskell и другие функциональные языки.

С каким япом ты бы сравнил Haskell и в чью пользу?

Джон Доу: Из более или менее распространенных — с Erlang. Но на Erlang все же проще писать и его проще выучить, как мне кажется.

Денис Мирзоев: Я хорошо знаю C, С++, Java и Haskell. C++ даже сравнивать ни с чем не надо, язык ужасен. Си — хороший язык для низкоуровневой разработки. В этой нише он будет лучше. В остальном я бы предпочёл Haskell.

Выбор между Java и Haskell уже труднее, но тут тоже нужно смотреть на конкретную задачу. Под андроид на Haskell будет скорее всего трудно писать, в этом случае лучше Java. А вот сервера писать на Haskell почти также удобно как и на Java. Если окружение позволяет — тулинг, доступность библиотек, — то я обычно выбираю Haskell.

Doctor_Ryner: С C#, достаточно погуглить, как реализовать Maybe на C# и на Haskell. Очень странно, что диктаторский чисто функциональный Haskell чувствуется гораздо более гибким и свободным. На самом деле это две крайности.

С# один из самых объектно-ориентированных языков, и преимущества Haskell сильно контрастируют с ним. В С# тебе постоянно приходится писать кучу лишних вещей, и это все очень неприятно. Использование функций высшего порядка может уродовать код в плане синтаксиса. На фоне всего этого уже сложно вернуться с коротких и элегантных решений Haskell

Игорь Шевнин: С Rust, пока что в пользу Rust. Он берёт очень многое от Haskell и других ФП-языков, но при этом дружит функциональный подход с императивным, а разработчики и сообщество намного грамотнее и более последовательно подошли к развитию языка с самого начала.

Что думаете про сообщество хаскелистов?

Джон Доу: Подавляющее большинство — очень дружелюбные люди, всегда готовые помочь. Приятное отличие от сообществ многих других языков.

Doctor_Ryner: Haskell сообщества зачастую содержат в себе пугающе умных людей, которые всегда готовы помочь. Не зря ходят локальные мемы про PhD, теорию категорий и академиков. Если заходя в чат по другим языкам, ты видишь, что люди обсуждают обычные production-проблемы и структуры данных. В чате по Haskell перед тобой сразу выскакивают монады, лемма Йонеды, аппликативные функторы, написание безумных типов и прочее-прочее.

Ты сразу видишь столько нового, чего ты не знал раньше — безумные композиции, элегантные преобразования и трансформации, решения проблем, что занимают на мейнстримных языках десятки строчек, чуть ли не в одну строчку.

Говорят, хаскелисты высокомерные. Правда?

Денис Мирзоев: Да. Мне кажется высокомерность связана с тем, что они очень любят свой язык и расстроены его недооценённостью.

Джон Доу: Нифига подобного.

Doctor_Ryner: Скорее всего такое мнение пошло, из-за того, что многих мейнстримных разработчиков сильно раздражает, когда хаскелисты начинают говорить о функциональном программировании и его преимуществах. Жуткое непонимание в свою очередь может раздражать самого хаскелиста, и он начнет бросаться терминами, за что его клеймят чсв.

Игорь Шевнин: Высокомерие — слишком сильное слово. Тут скорее дело в том, что ФП, ООП, различие ООП-классов и union types, extension problem и много других понятий однажды складываются в очень чёткую картинку, и после этого становится трудно воспринимать людей, которые пытаются противопоставить ООП и ФП или другим образом представить широкую проблему в узкой перспективе.

Почему ФП-языки до сих пор нишевые?

Денис Мирзоев: Их преимуществ всё ещё не достаточно, чтобы заинтересовать большое количество программистов. Трудность в изучении не способствует популярности. Проблемы с тулингом тоже многих отпугивают, а мне кажется как раз увеличение численности сообщества могло бы решить эту проблему. Получается замкнутый круг.

Игорь Шевнин: Нишевость постепенно проходит, а функциональные концепции подтягиваются в другие языки.

Doctor_Ryner: Сами функциональные принципы и языки, что их поддерживают, уже повсеместны. Даже для шарпов есть Linq и некоторые другие библиотеки. Нишевые скорее чисто функциональные языки, так как в них используются нестандартные концепции.

Не стоит забывать, что еще лет 20 назад железо было не достаточно производительным для функциональных языков, так что в мейнстрим функциональщина начала входить в последние годы, а интерес к Haskell только растет.

Источник

Через тернии к Haskell. 1/2

haskell комментарии в коде. 68233f00ef8fd54b93ea5495ba5fd3a1. haskell комментарии в коде фото. haskell комментарии в коде-68233f00ef8fd54b93ea5495ba5fd3a1. картинка haskell комментарии в коде. картинка 68233f00ef8fd54b93ea5495ba5fd3a1. В этой главе мы встретимся с условными конструкциями, выглянем в терминал, а также узнаем, почему из Haskell-функций не возвращаются (впрочем, последнее — не более чем игра слов).

Первая часть короткого и жесткого введения в Haskell. Вторую часть можно найти здесь

Основа мейнстримовых языков очень похожа

Haskell сильно от них отличается. Этот язык использует кучу концепций, о которых я раньше не слышал. Многие из этих концепций помогут вам стать лучшим программистом.

Но изучение Haskell-а может быть сложным. По крайней мере оно было сложным для меня. И в этой статье я постараюсь показать, чего мне не хватало в процессе изучения.

Эту статью будет сложно осилить. Это было сделано специально. В изучении Haskell нет простых путей. Эта дорога трудна и заковыриста. Но я думаю, что это и хорошо. Потому что именно сложность Haskell-а делает его интересным.

Обычный метод изучения Haskell-а состоит в прочтении двух книг.
Сначала “Learn You a Haskell”, а потом “Real World Haskell”.

Я согласен с тем, что это правильный подход к обучению. Но для того, чтобы понять, что из себя представляет Haskell, вам придется очень внимательно прочитать эти книги.

С другой стороны, эта статья очень краткое и сжатое ввдение во все основные аспекты Haskell. Я также добавил некоторые нюансы, которых мне не хватало при изучении Haskell.

Статья состоит из пяти частей:

Какие-то примеры могут не работать, но большинство должно.
Чуть ниже вы должны увидеть ссылку на файл

Введение

Установка

haskell комментарии в коде. image loader. haskell комментарии в коде фото. haskell комментарии в коде-image loader. картинка haskell комментарии в коде. картинка image loader. В этой главе мы встретимся с условными конструкциями, выглянем в терминал, а также узнаем, почему из Haskell-функций не возвращаются (впрочем, последнее — не более чем игра слов).

Без паники

haskell комментарии в коде. 8092382c36b2b005e7035507a2caaee8. haskell комментарии в коде фото. haskell комментарии в коде-8092382c36b2b005e7035507a2caaee8. картинка haskell комментарии в коде. картинка 8092382c36b2b005e7035507a2caaee8. В этой главе мы встретимся с условными конструкциями, выглянем в терминал, а также узнаем, почему из Haskell-функций не возвращаются (впрочем, последнее — не более чем игра слов).

Многие книги и статьи начинают знакомство с Haskell-ом с каких-то эзотерических формул (быстрая сортировка, Фибоначчи и т.п.). Я поступлю по-другому. Я не буду сразу показывать суперсилу Haskell-а. Я сконцентрируюсь на тех вещах, в которых Haskell похож на остальные языки программирования. Итак, давайте начнем с привычного “Hello World”.

Чтобы запустить его, сохраните этот код как hello.hs и выполните:

Вы можете скачать исходник по ссылке, которая находится под «Введением»

Сохраняем файл 00_hello_world.lhs и выполняем:

А теперь напишем программу, которая запрашивает ваше имя и говорит “Привет, имярек!”:

Для начала, сравним наш пример с другими императивными языками:

Структура программ очень похожа, но есть небольшие синтаксические отличия. Основная часть туториала будет посвящена именно этим отличиям.

Но не забывайте о том, что Haskell может выглять как обычный императивный язык.

Самый базовый Haskell

haskell комментарии в коде. f92f4e7843c7779f0a92f436cb28f2f0. haskell комментарии в коде фото. haskell комментарии в коде-f92f4e7843c7779f0a92f436cb28f2f0. картинка haskell комментарии в коде. картинка f92f4e7843c7779f0a92f436cb28f2f0. В этой главе мы встретимся с условными конструкциями, выглянем в терминал, а также узнаем, почему из Haskell-функций не возвращаются (впрочем, последнее — не более чем игра слов).

Перед тем, как мы продолжим, хотелось бы предупредить вас о некоторых характерных особенностях Haskell.

Haskell — это функциональный язык.
Если вы начинали с императивных языков, вам придется выучить много новых вещей.
К счастью, многие из эти концепций помогут вам лучше программировать даже на императивных языках.

Продвинутая статическая типизация

Вообще говоря, ваши функции ничего не будут изменять во внешнем мире.
С одной строны, это значит что функции не могут изменять значение переменной, не могут получать данные от пользователя, не могут писать на экране, не могут запускать ракеты.
С другой стороны, мы получаем легкое распараллеливание наших программ.
Haskell проводит четкую границу между чистыми функциями и функциями, производящими побочные эффекты.
При таком подходе становится намного легче анализировать свойства вашей программы.
В функционально чистых частях программы многие ошибки будут исключены еще на этапе компиляции.

Более того, функционально чистые функции следуют основному закону Haskell-а:

Вызов функции с одними и теми же параметрами всегда даст один и тот же результат.

Ленивость по умолчанию очень необычная вещь в языках программирования.
По умолчания, Haskell вычисляет значение только тогда, когда оно реально необходимо.
Как следствие, мы получаем возможность очень просто работать с бесконечными структурами данных.

Определение функций

Вы можете определять функции следующим образом:

Не забывайте, что в Haskell интенсивно используются функции и типы.
И поэтому их очень просто определять.
Синтаксис языка был продуман с тем, чтобы сделать определения максимально простыми.

Пример создания нового типа

Обычно при написании программ, вы указываете тип функции.
Но это не обязательно.
Компилятор достаточно умен, чтобы сделать это за вас.

Давайте немного поиграемся.

Вы получите ошибку:

Проблема заключается в том, что 4.2 это не Int.

Но какой тип мы должны указать?
Чтобы понять, какой тип для нас вывел Haskell, просто запустите ghci:

Хмм… Что это за странный тип?

Классы типов — очень мощный элемент языка.
С их помощью мы можем творить удивительные вещи.
Но об этом мы поговорим чуть позже.

Мде, странновато.
На самом деле в Haskell-е ни одна функция не принимает на вход два аргумента.
Вместо этого у всех функций — только один аргумент.
Но обратите внимание, что функцию от двух аргументов можно представить как функцию, принимающую первый аргумент и возвращающую функцию, принимающую второй аргумент в качестве параметра.
.

Для создания функция также можно использовать другую запись.
Лямбда-выражения позволяют создавать функции не присваивая им имен.
Их также назызвают анонимными функциями.
Мы можем записать:

Если функциональное программирование вам в новинку, к этому моменту ваши мозги начинают закипать.
Самое время написать реальное приложение.

Но прежде, чем мы начнем, надо проверить, что система типов работает как следует:

Этот код работает потому, что 3 может представлять как Fractional (дробное) число типа Float, так и целое типа Integer.
Поскольку 2.4 имеет тип Fractional, 3 тоже представляется в виде Fractional числа.

Если мы пропишем в функции другие типы, она перестанет работать:

Компилятор сигнализирует об ошибке.
Два параметра должны иметь одинаковый тип.

Если вы считаете, что это плохая идея, и комплятор должен преобразовать типы за вас, вам действительно стоит посмотреть отличное и смешное видео:
WAT

Необходимый минимум Haskell

haskell комментарии в коде. e12b2ae8bb9fd52d4a586fcd82a44711. haskell комментарии в коде фото. haskell комментарии в коде-e12b2ae8bb9fd52d4a586fcd82a44711. картинка haskell комментарии в коде. картинка e12b2ae8bb9fd52d4a586fcd82a44711. В этой главе мы встретимся с условными конструкциями, выглянем в терминал, а также узнаем, почему из Haskell-функций не возвращаются (впрочем, последнее — не более чем игра слов).

Я советую вам бегло просмотреть этот раздел.
Думайте о нем, как о справочном материале.
Haskell — очень многоплановый язык.
Поэтому какие-то вещи в этом разделе будет пропущены.
По необходимости возвращайтесь сюда, если какие-то вещи покажутся вам странными или непонятными.

Выражения

Арифметические
Логические
Возведение в степень

Integer не имеет ограничений по величине, за исключением оперативной памяти машины:

О да!
А еще можно использовать рациональные числа!
Но для этого необходимо использовать модуль Data.Ratio :

Списки
Строки
Кортежи
Разбираемся со скобками

Полезные вещи для записи функций

Объявлять тип функции перед ее определеним необязательно.
Haskell сам выведет наиболее общий тип.
Но указание типа функции — правило хорошего тона.

Обратите внимание, что ^ используется в инфиксной нотации.
Для каждого инфиксного оператора существует возможность префиксной записи.
Просто заключите нужный оператор в скобки.

Мы можем убрать x из левой и правой части выражения!
Это называется η-редукция.

Обратите внимание, что мы можем использовать символ ‘ в имени функции.
Например:

Еще один эквивалент функции:

Внимание: выравнивание важно в программах на Haskell-е.
Как и в Python-е, неправильное выравнивание может сломать код!

Сложная часть

Приступим к изучению сложной части.

Функциональный стиль

haskell комментарии в коде. 4e3d976483399d95f86fff1d92e1c3b8. haskell комментарии в коде фото. haskell комментарии в коде-4e3d976483399d95f86fff1d92e1c3b8. картинка haskell комментарии в коде. картинка 4e3d976483399d95f86fff1d92e1c3b8. В этой главе мы встретимся с условными конструкциями, выглянем в терминал, а также узнаем, почему из Haskell-функций не возвращаются (впрочем, последнее — не более чем игра слов).
В этом разделе я покажу вам небольшой пример удивительных возможностей Haskell по рефакторингу кода.
Мы выберем проблему и решим ее стандартным императивным способом. Потом начнем понемногу улучшать этот код. Конечный результат будет гораздо проще для понимании и более элегантным.

Вот условие задачи, которую мы будем решать:

Имея список целых чисел, необходимо посчитать сумму четных чисел в списке.

Для того, чтобы показать разницу между функциональным и императивным подходом, сначала напишем императивное решение (на Javascript):

Но в Haskell-е отсутствуют переменные и циклы.
Без использования циклов тот же результат можно получить, используя рекурсию.

Замечание:
Рекурсию в императивных языках имеет репутацию медленного инструмента. Но для большинства функциональных языков это не так. В большинстве случаев, Haskell очень качественно оптимизирует работу с рекурсивными функциями.

Внимательно посмотрите на этот код. Потому что мы будем переводить его на Haskell.
Но прежде я покажу вам три простые, но очень полезные функции, которые мы будем использовать:

even проверка на четность.

head возвращает первый элемент списка:

tail возвращает все элементы списка, окромя первого:

Итак, первое решение задачи на Haskell-е.
Функция evenSum возвращает сумму всех четных чисел в списке:

Чтобы протестировать функцию, просто запустите ghci :

Ниже представлен пример работы функции (Я жульничаю. В курсе. К вопросу о нестрогих вычислениях мы вернемся позже):

С точки зерния императивного языка все выглядит нормально. Но тут кроется большой простор для улучшений. Для начала, мы можем сделать тип более общим.

А теперь используем сопоставление с образцом.

Что такое сопоставление с образцом (pattern matching)? Это просто ипользование значений вместо именованных параметров. (Для самых смелых подробное описание сопоставления с образцом можно изучитьздесь)

Вместо такого кода: foo l = if l == [] then else
Можно просто написать:

Но сопоставление с образцом может гораздо большее. Оно может анализировать внутренние данные сложной структуры. Мы можем заменить

Очень полезная возможность.
Она делает наш код более кратким и читаемым.

Вы можете упростить запись функций в Haskell-е применяя η-редукцию.
Например, вместо такой записи:

Мы можем использовать этот подход для того, чтобы избавиться от l :

Функции Высшего Порядка

haskell комментарии в коде. image loader. haskell комментарии в коде фото. haskell комментарии в коде-image loader. картинка haskell комментарии в коде. картинка image loader. В этой главе мы встретимся с условными конструкциями, выглянем в терминал, а также узнаем, почему из Haskell-функций не возвращаются (впрочем, последнее — не более чем игра слов).

Чтобы сделать нашу функцию еще более красивой, мы можем применить ФВП (функции высшего порядка).
Вы спрашиваете, что это за звери?
Функции высшего порядка — это функции, принимающие функции в качестве параметра.

Будем продвигаться небольшими шагами.

Код выше можно заменить на:

Если вы действительно хотите разобраться в происходящей магии,
реализация foldl приведена ниже.

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

Теперь наша версия evenSum выглядит так:

И конечно, мы можем провести следующую замену

foldl’ не самая интуитивно понятная функция. Но с ней обязательно стоит разобраться.

Для этого проведем пошаговый анализ:

Мы можем ее использовать, чтобы еще дальше η-редуцировать нашу функцию:

Можно сделать еще кое-что, чтобы код стал чище:

Обсудим получившийся результат.
Что мы получили, применив функции высшего порядка?

В глаза бросается краткость кода. Но основной плюс в том, насколько проще стало понимать нашу программу. Допустим, мы хотим немного изменить нашу функцию. Теперь нам хочется, чтобы она возвращала сумму квадратов всех четных чисел в списке.

Добавить это изменение в версию 10 очень просто:

Нам надо просто добавить еще одну “преобразовывающую функцию” (Обратите внимание, что squareEvenSum» эффективнее двух других реализаций. Порядок (.) действительно имеет значение.).

Функция map просто применяет функцию-параметр ко всем элементам списка.

Если вы думаете, что сделать код более абстрактным сложно, то вы сильно ошибаетесь.Например, эту функцию можно использовать не только для списков, но и для любых рекурсивных типов. Если вам интересно как — почитайте эту забавную статью: Functional Programming with Bananas, Lenses, Envelopes and Barbed Wire by Meijer, Fokkinga and Paterson.

На нашем примере можно увидеть, насколько мощным может быть функциональное программирование. К сожалению в некоторых случаях чисто функциональные языки не всегда самый лучший выбор. По крайней мере, действительно универсальный функциональный язык еще не создан.

Одна из отличительных особенностей Haskell лежит в создании DSL-ей (Доменно Ориентированных Языков).

На самом деел, Haskell является отличным инструментом, даже если вы хотите писать программы в императивном стиле. Когда я изучал Haskell, эта мысль стала для меня откровением.

Но перед тем, как мы перейдем к разговорам о суперсиле Haskell-а, нам надо обсудить еще один существенный аспект — Типы.

haskell комментарии в коде. 0f17b4ef993ce2b9da74e47c8bb1402e. haskell комментарии в коде фото. haskell комментарии в коде-0f17b4ef993ce2b9da74e47c8bb1402e. картинка haskell комментарии в коде. картинка 0f17b4ef993ce2b9da74e47c8bb1402e. В этой главе мы встретимся с условными конструкциями, выглянем в терминал, а также узнаем, почему из Haskell-функций не возвращаются (впрочем, последнее — не более чем игра слов).

Haskell — язык со строгой статической типизацией.

Почему это так важно? Потому что это сильно уменьшает количество ошибок.
В Haskell, большинство ошибок можно обнаружить еще на этапе компиляции. И происходит это потому, что во время компиляции активно применяется вывод типов.
Если вы используете не тот параметр не в том месте, компилятор это легко заметит.

Вывод типов

Статическая типизация необходима для создания быстрых программ
Но большинство статически типизированных языков слабы в обобщении концепций.

Одна из благодатных возможностей Haskell-а вывод типов.

Простой пример.
Функция square на языке Haskell:

x :+ y это запись комплексного числа (x + iy).

Теперь сравните количество кода по сравнению с программой на C:

Для каждого типа надо писать свою функцию. Обойти это можно только используя техники метапрограммирования. Например используя препроцессор. C++ предлагает более красивый вариант решения, используя шаблоны:

Вариант на C++ выглядит гораздо лучше C.
Но для функций комплексной переменной будет сложно сохранить такой же синтаксис. Пример можно найти в
этой статье
.

В C++ вы должны описать функцию, которая может работать с разными типами.
В Haskell ситуация противоположная.
Функция будет максимально обобщенной по умолчанию.

Вывод типов Haskell дает ощущение свободы, которое представляют динамически типизированные языки.
Но в отличие от динамически типизированных языков, большинство ошибок, отлавливаются еще до момента выполнения программы.
Люди говорят про Haskell так:

“Если оно компилируется, то оно скорее всего работает правильно”

Создание новых типов

Вы можете создавать свои собственные типы. Например, можно объявлять синонимы типов.

Но это не сильно защитит вас от ошибок.
Попробуйте поменять местами два параметра функции showInfos и запустить программу:

Если в showInfos поменять параметры местами, компилятор начнет ругаться. Возможность ошибиться исчезла. Но ценой увеличения количества кода.

Обратите внимание, что конструкторы типа — это функции :

Использование data обычно выглядит так:

Для
DataTypeName и DataTypeConstructor обычно используют одно и то же имя.
Например:

Также можно использовать синтаксис записей:

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

Рекурсивные типы

Вы уже встречались с представителем рекурсивных типов — со списками. Можно создать списки с нуля, используя более многословный синтаксис:

Для более простой записи можно использовать инфиксный конструктор типа.

Если вы хотите печатать ( Show ), считывать ( Read ), проверять на равенство ( Eq ) и сравнивать ( Ord ) значения вашего нового типа, вы можете сказать Haskell-у, чтобы он автоматически вывел необходимые для этого функции.

Деревья

haskell комментарии в коде. 036ef55dd4b1a72d4a2d0d224e6e52c8. haskell комментарии в коде фото. haskell комментарии в коде-036ef55dd4b1a72d4a2d0d224e6e52c8. картинка haskell комментарии в коде. картинка 036ef55dd4b1a72d4a2d0d224e6e52c8. В этой главе мы встретимся с условными конструкциями, выглянем в терминал, а также узнаем, почему из Haskell-функций не возвращаются (впрочем, последнее — не более чем игра слов).

Вот еще один стандартный пример: двоичные деревья.

Давайте напишем функцию, которая преобразует список в упорядоченное двоичное дерево.

Оцените элегантность этой функции.
Перевод на русский язык:

Должно получиться что-то вроде:

Это информативное, но не очень красивое представление нашего дерева.

Чтобы немного развлечься, давайте напишем код для более красивого отображения наших деревьев. Я получил большое удовольствие от написания функции, которая может красиво отображать любые деревья. Если эта часть покажется вам сложной — можете спокойно ее пропустить.

Вот моя версия отображения бинарного дерева. Она не так страшна, как выглядит.Я адаптировал ее для отображения гораздо более странных объектов.

Метод treeFromList не изменяется.

А теперь мы можем поиграться:

А поскольку мы можем проверять деревья на равенство и задавать их порядок, мы можем создать дерево деревьев!

haskell комментарии в коде. f7abb94efe490cd5a5db9ab48ff3f378. haskell комментарии в коде фото. haskell комментарии в коде-f7abb94efe490cd5a5db9ab48ff3f378. картинка haskell комментарии в коде. картинка f7abb94efe490cd5a5db9ab48ff3f378. В этой главе мы встретимся с условными конструкциями, выглянем в терминал, а также узнаем, почему из Haskell-функций не возвращаются (впрочем, последнее — не более чем игра слов).

Что эквивалентно следующему:

Посмотрите на красоту и мощь этого типа. Мы можем создавать деревья состоящие не только из чисел, строк и символов, но так же из других деревьев. Мы можем даже создать дерево, элементами которого будут деревья деревьев!

Бесконечные структуры

haskell комментарии в коде. 2137c1814b908674c5b5c7eddac11fa4. haskell комментарии в коде фото. haskell комментарии в коде-2137c1814b908674c5b5c7eddac11fa4. картинка haskell комментарии в коде. картинка 2137c1814b908674c5b5c7eddac11fa4. В этой главе мы встретимся с условными конструкциями, выглянем в терминал, а также узнаем, почему из Haskell-функций не возвращаются (впрочем, последнее — не более чем игра слов).
Haskell часто называют ленивым языком.

Но правильно говорить, что Haskell это не строгий язык. Ленивость это просто популярная реализация нестрогих языков.

Что такое нестрогий язык? Из Haskell-wiki:

Редукция (математический термин для вычисления выражения) идет снаружи внутрь.

Например используя Haskell можно сделать так:

И программа завершается.

Обратите внимание на синтаксис для задания бесконечных списков в Haskell

Допустим, мы не против иметь упорядоченное двоичное дерево. Вот пример бесконечного двоичного дерева:

Полноценное двоичное дерево с нулями в каждом узле. Теперь я докажу вам, что мы можем работать с этим деревом, используя следующую функцию:

Давайте посмотрим, что выведет эта программа:

Этот код компилируется, запускается, останавливается и выводит следующий результат:

Чтобы немного раскачать нейроны, сделаем дерево более странным:

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *