Haskell — это чисто функциональный, статически типизированный язык программирования, названный в честь логика Хаскелла Карри.
Он был разработан в 1980-х годах группой исследователей, чтобы создать основанный на исследовании язык, объединяющий лучшие идеи функционального программирования.
Язык популярен как в академической среде, так и в промышленной разработке (в основном в финансах, криптографии, компиляторах и системах с высокой надёжностью).
Основные черты Haskell:
1. Чисто функциональный
Функции в Haskell не имеют побочных эффектов (например, изменения глобальных переменных, ввода-вывода и т.д.).
2. Ленивые вычисления
Выражения вычисляются только тогда, когда это необходимо.
3. Статическая типизация
Типы проверяются на этапе компиляции.
4. Полиморфизм и типовые классы
Поддержка алгебраических типов данных (ADT) и типовых классов (class, instance), похожих на интерфейсы в других языках.
5. Монады
Монады — это мощный инструмент для работы с побочными эффектами, например, вводом-выводом (IO), обработкой ошибок (Maybe, Either), состоянием (State).
➕ Плюсы Haskell
- Высокий уровень абстракции.
- Безопасность типов.
- Лёгкость тестирования и рефакторинга.
- Мощная система типов.
- Отличен для написания сложной логики и прототипов.
➖Минусы Haskell
- Крутая кривая обучения.
- Меньше библиотек и ресурсов по сравнению с популярными языками.
- Меньше производительности в некоторых задачах.
- Сложности с отладкой ленивых вычислений.
Он был разработан в 1980-х годах группой исследователей, чтобы создать основанный на исследовании язык, объединяющий лучшие идеи функционального программирования.
Язык популярен как в академической среде, так и в промышленной разработке (в основном в финансах, криптографии, компиляторах и системах с высокой надёжностью).
Основные черты Haskell:
1. Чисто функциональный
Функции в Haskell не имеют побочных эффектов (например, изменения глобальных переменных, ввода-вывода и т.д.).
2. Ленивые вычисления
Выражения вычисляются только тогда, когда это необходимо.
3. Статическая типизация
Типы проверяются на этапе компиляции.
4. Полиморфизм и типовые классы
Поддержка алгебраических типов данных (ADT) и типовых классов (class, instance), похожих на интерфейсы в других языках.
5. Монады
Монады — это мощный инструмент для работы с побочными эффектами, например, вводом-выводом (IO), обработкой ошибок (Maybe, Either), состоянием (State).
➕ Плюсы Haskell
- Высокий уровень абстракции.
- Безопасность типов.
- Лёгкость тестирования и рефакторинга.
- Мощная система типов.
- Отличен для написания сложной логики и прототипов.
➖Минусы Haskell
- Крутая кривая обучения.
- Меньше библиотек и ресурсов по сравнению с популярными языками.
- Меньше производительности в некоторых задачах.
- Сложности с отладкой ленивых вычислений.
ghcup
Это инструмент управления Haskell, расшифровывается как GHC Up (Glasgow Haskell Compiler Up).
Он предназначен для установки, обновления и управления различными версиями компилятора GHC, а также других инструментов Haskell, таких как:
- GHC (Glasgow Haskell Compiler) — основной компилятор Haskell.
- Cabal (Haskell Package Manager) — менеджер пакетов.
- Stack — альтернативный инструмент сборки и управления зависимостями.
- HLS (Haskell Language Server) — сервер для поддержки языка в редакторах.
Зачем нужен ghcup?
Когда вы работаете с Haskell, у вас может быть несколько проектов, требующих разных версий GHC или других инструментов. ghcup позволяет:
- Устанавливать несколько версий GHC параллельно.
- Переключаться между ними в зависимости от проекта.
- Устанавливать и обновлять HLS и другие инструменты.
- Управлять системой Haskell в удобном и централизованном виде.
Установка ghcup
После установки ghcup автоматически настроит переменные окружения и предложит добавить пути в ~/.bashrc, ~/.zshrc и т.д.
Это инструмент управления Haskell, расшифровывается как GHC Up (Glasgow Haskell Compiler Up).
Он предназначен для установки, обновления и управления различными версиями компилятора GHC, а также других инструментов Haskell, таких как:
- GHC (Glasgow Haskell Compiler) — основной компилятор Haskell.
- Cabal (Haskell Package Manager) — менеджер пакетов.
- Stack — альтернативный инструмент сборки и управления зависимостями.
- HLS (Haskell Language Server) — сервер для поддержки языка в редакторах.
Зачем нужен ghcup?
Когда вы работаете с Haskell, у вас может быть несколько проектов, требующих разных версий GHC или других инструментов. ghcup позволяет:
- Устанавливать несколько версий GHC параллельно.
- Переключаться между ними в зависимости от проекта.
- Устанавливать и обновлять HLS и другие инструменты.
- Управлять системой Haskell в удобном и централизованном виде.
Установка ghcup
curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh
После установки ghcup автоматически настроит переменные окружения и предложит добавить пути в ~/.bashrc, ~/.zshrc и т.д.
Как создать простой проект
В каталоге проектов набираем (name это имя проекта)
Для создания проекта можно использовать схему simple - она создаёт меньше файлов в проекте.
Сборка
После этого внутри каталога .stack-work появиться исполняемый файл, его можно запустить так
Форматировать (отступы и всякое такое) файл
Установить форматтер можно командой:
Форматировать каталог
В каталоге проектов набираем (name это имя проекта)
stack new name
Для создания проекта можно использовать схему simple - она создаёт меньше файлов в проекте.
stack new name simple
Сборка
stack build
После этого внутри каталога .stack-work появиться исполняемый файл, его можно запустить так
stack exec name
Форматировать (отступы и всякое такое) файл
fourmolu -i Module.hs
Установить форматтер можно командой:
stack install fourmolu
Форматировать каталог
fourmolu -i src
Что такое функция?
В языке Haskell функция — это основная единица вычисления и центральный элемент языка. Это чистое, математическое отображение входных значений в выходные, без побочных эффектов, и с мощной поддержкой композиции, полиморфизма и каррирования.
Функции в Haskell следуют трём обязательным правилам:
1. все функции должны принимать аргументы,
2. все функции должны возвращать значение,
3. функция возвращает один и тот же результат для одного и того же набора аргументов (чистота, ссылочная прозрачность).
Функции в Haskell не могут иметь побочных эффектов (на самом деле могут, но это нужно специально указывать).
Функции в Haskell — это значения, как числа или строки. Их можно:
- Передавать как аргументы другим функциям.
- Возвращать из функций.
- Присваивать переменным.
- Хранить в структурах данных.
В Haskell все функции по умолчанию каррированы — то есть функция от нескольких аргументов на самом деле является функцией от одного аргумента, возвращающей другую функцию.
Примеры функций
Простая функция:
Функция высшего порядка (принимает другую функцию):
Рекурсивная функция:
В языке Haskell функция — это основная единица вычисления и центральный элемент языка. Это чистое, математическое отображение входных значений в выходные, без побочных эффектов, и с мощной поддержкой композиции, полиморфизма и каррирования.
Функции в Haskell следуют трём обязательным правилам:
1. все функции должны принимать аргументы,
2. все функции должны возвращать значение,
3. функция возвращает один и тот же результат для одного и того же набора аргументов (чистота, ссылочная прозрачность).
Функции в Haskell не могут иметь побочных эффектов (на самом деле могут, но это нужно специально указывать).
Функции в Haskell — это значения, как числа или строки. Их можно:
- Передавать как аргументы другим функциям.
- Возвращать из функций.
- Присваивать переменным.
- Хранить в структурах данных.
В Haskell все функции по умолчанию каррированы — то есть функция от нескольких аргументов на самом деле является функцией от одного аргумента, возвращающей другую функцию.
Примеры функций
Простая функция:
square :: Int -> Int
square x = x * x
Функция высшего порядка (принимает другую функцию):
applyTwice :: (a -> a) -> a -> a
applyTwice f x = f (f x)
Рекурсивная функция:
factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * factorial (n - 1)
Алонзо Чёрч (Alonzo Church, 14 июня 1903 — 11 августа 1995) — выдающийся американский математик и логик, один из основоположников теоретической информатики и математической логики. Оказал огромное влияние не только на математику и логику, но и на зарождение информатики как науки.
Лямбда-исчисление
В 1930-х годах Чёрч разработал лямбда-исчисление — формальную систему для описания функций и вычислений. Эта система стала фундаментом для функционального программирования и повлияла на развитие многих языков программирования, таких как Lisp, Haskell, ML и другие.
Тезис Чёрча — Тьюринга
Вместе с Аланом Тьюрингом он сформулировал тезис Чёрча — Тьюринга, утверждающий, что любая функция, которую можно "вычислить алгоритмически", может быть вычислена либо с помощью машины Тьюринга, либо в рамках лямбда-исчисления. Этот тезис лёг в основу теории вычислимости.
Теорема Чёрча о неразрешимости
В 1936 году он доказал, что проблема разрешения (Entscheidungsproblem) — задача определения истинности любого логического утверждения в рамках формальной системы — неразрешима. Это было сделано независимо и почти одновременно с Тьюрингом.
Логика и философия
Чёрч внёс значительный вклад в интенсиональную логику и разработал так называемую "теорию типов Чёрча", а также занимался философскими вопросами смысла и референции.
Лямбда-исчисление
В 1930-х годах Чёрч разработал лямбда-исчисление — формальную систему для описания функций и вычислений. Эта система стала фундаментом для функционального программирования и повлияла на развитие многих языков программирования, таких как Lisp, Haskell, ML и другие.
Тезис Чёрча — Тьюринга
Вместе с Аланом Тьюрингом он сформулировал тезис Чёрча — Тьюринга, утверждающий, что любая функция, которую можно "вычислить алгоритмически", может быть вычислена либо с помощью машины Тьюринга, либо в рамках лямбда-исчисления. Этот тезис лёг в основу теории вычислимости.
Теорема Чёрча о неразрешимости
В 1936 году он доказал, что проблема разрешения (Entscheidungsproblem) — задача определения истинности любого логического утверждения в рамках формальной системы — неразрешима. Это было сделано независимо и почти одновременно с Тьюрингом.
Логика и философия
Чёрч внёс значительный вклад в интенсиональную логику и разработал так называемую "теорию типов Чёрча", а также занимался философскими вопросами смысла и референции.
Как создать простой проект шаг за шагом
В каталоге проектов набираем
это создаст каталог шаблона простого проекта. Заглянем внутрь:
Нас интересует файл
Внутри там написан простой
Соберём ...
... и запустим его
Далее в файле
В каталоге проектов набираем
stack new firstpro simple
это создаст каталог шаблона простого проекта. Заглянем внутрь:
|- src
|- - Main.hs
|- CHANGELOG.md
|- LICENSE
|- README.md
|- Setup.hs
|- firstpro.cabal
|- stack.yaml
Нас интересует файл
Main.hs - это отправная точка для работы нашего проекта.Внутри там написан простой
hello worldmodule Main (main) where
main :: IO ()
main = do
putStrLn "hello world"
Соберём ...
stack build
... и запустим его
stack exec firstpro
Далее в файле
Main.hs можно добавлять свои функции, добавлять свои модули в проект, но это уже совсем другая история.👍1
Haskell Playground
Что делать, если нет возможности или желания ставить haskell на комп, но нужно что-то попрогать? Для этого есть онлайн песочницы.
Для haskell это https://play.haskell.org/
Как и многие подобные платформы она позволяет выбрать версию компилятора и уровень оптимизации, посмотреть ассемблерный код и код, транслируемый в haskell core (это внутренний язык компилятора GHC).
Что делать, если нет возможности или желания ставить haskell на комп, но нужно что-то попрогать? Для этого есть онлайн песочницы.
Для haskell это https://play.haskell.org/
Как и многие подобные платформы она позволяет выбрать версию компилятора и уровень оптимизации, посмотреть ассемблерный код и код, транслируемый в haskell core (это внутренний язык компилятора GHC).
Haskell Core
Это внутренний язык компилятора GHC (Glasgow Haskell Compiler) языка Haskell. Он представляет собой расширение системы полиморфного λ-исчисления высших порядков System Fω.
Core используется для трансляции кода на Haskell в промежуточное представление, которое упрощает анализ программ и оптимизацию.
Код на Haskell транслируется в Core во время компиляции. Это происходит с помощью дешугаринга (убираем синтаксический сахар) — перевода программы, которая использует многие конструкции языка, в программу, использующую только несколько.
Core имеет гораздо меньше синтаксических форм по сравнению с исходным языком Haskell.
Некоторые особенности Core:
- Привязки функций — всегда имеют одно имя переменной в левой части, преобразуются в лямбды.
- Многоаргументные функции — переводятся во вложенные лямбды, в Core все лямбды — одноаргументные.
- Полиморфные функции — в Core нужно указывать аргументы типов, в исходном Haskell — только аргументы значений.
- Конструкторы данных — например, при построении кортежа из трёх элементов в Core передаются шесть аргументов: сначала типы элементов, затем элементы.
Это внутренний язык компилятора GHC (Glasgow Haskell Compiler) языка Haskell. Он представляет собой расширение системы полиморфного λ-исчисления высших порядков System Fω.
Core используется для трансляции кода на Haskell в промежуточное представление, которое упрощает анализ программ и оптимизацию.
Код на Haskell транслируется в Core во время компиляции. Это происходит с помощью дешугаринга (убираем синтаксический сахар) — перевода программы, которая использует многие конструкции языка, в программу, использующую только несколько.
Core имеет гораздо меньше синтаксических форм по сравнению с исходным языком Haskell.
Некоторые особенности Core:
- Привязки функций — всегда имеют одно имя переменной в левой части, преобразуются в лямбды.
- Многоаргументные функции — переводятся во вложенные лямбды, в Core все лямбды — одноаргументные.
- Полиморфные функции — в Core нужно указывать аргументы типов, в исходном Haskell — только аргументы значений.
- Конструкторы данных — например, при построении кортежа из трёх элементов в Core передаются шесть аргументов: сначала типы элементов, затем элементы.
Переменные
Математические аналогии в Haskell не заканчиваются на функциях. В Haskell не переменные, а объявления, или константные переменные, если брать аналогию из других языков.
Например, записать где-то
не получится, компилятор откажется такое компилировать.
Если рассматривать переменные, как естественный способ упрощения записи вычислений, то Haskell предлагает 2 варианта, которые можно комбинировать - блоки let и where:
или
такая запись больше походит на определение математической формулы и привычнее для использования в академических задачах.
Математические аналогии в Haskell не заканчиваются на функциях. В Haskell не переменные, а объявления, или константные переменные, если брать аналогию из других языков.
Например, записать где-то
x = 3
...
x = 5
не получится, компилятор откажется такое компилировать.
Если рассматривать переменные, как естественный способ упрощения записи вычислений, то Haskell предлагает 2 варианта, которые можно комбинировать - блоки let и where:
calcChange :: Int -> Int -> Int
calcChange owed given =
let change = given - owed
in
if change > 0
then change
else 0
или
calcChange :: Int -> Int -> Int
calcChange owed given = if change > 0
then change
else 0
where change = given - owed
такая запись больше походит на определение математической формулы и привычнее для использования в академических задачах.
🔥1
GHCI
GHCi — это интерактивная среда (REPL — Read-Eval-Print Loop) для языка программирования Haskell, входящая в состав компилятора GHC (Glasgow Haskell Compiler).
Основные функции GHCi
- Выполнение Haskell-выражений в реальном времени;
- Можно вводить выражения, функции, определения и сразу видеть результат;
- Загрузка и тестирование модулей;
- Можно загрузить .hs-файлы (например,
- Отладка и инспекция типов. GHCi позволяет проверять типы выражений с помощью команды
Когда GHCi особенно полезен
- изучение Haskell;
- быстрое прототипирование кода;
- тестирование функций без компиляции всего проекта.
Полезные команды
Пример использование GHCI
Запускаем интерпретатор командой
и далее пишем что хотим
GHCi — это интерактивная среда (REPL — Read-Eval-Print Loop) для языка программирования Haskell, входящая в состав компилятора GHC (Glasgow Haskell Compiler).
Основные функции GHCi
- Выполнение Haskell-выражений в реальном времени;
- Можно вводить выражения, функции, определения и сразу видеть результат;
- Загрузка и тестирование модулей;
- Можно загрузить .hs-файлы (например,
:load MyModule.hs) и работать с их содержимым;- Отладка и инспекция типов. GHCi позволяет проверять типы выражений с помощью команды
:type (сокращённо :t).Когда GHCi особенно полезен
- изучение Haskell;
- быстрое прототипирование кода;
- тестирование функций без компиляции всего проекта.
Полезные команды
:load / :l - загрузить файл:reload / :r - перезагрузить текущий файл:type / :t - показать тип выражения:info / :i - информация о функции, типе или классе типов:help / :? - справка по командам:quit / :q - выйти из GHCi:set +t - После выполнения команды интерпретатор будет выводить на экран не только результат вычисления выражения, но и его тип.:set +s - После выполнения команды интерпретатор будет выводить на экран не только результат вычисления выражения, но и статистику вычислений.Пример использование GHCI
Запускаем интерпретатор командой
ghci
и далее пишем что хотим
ghci> let factorial 0 = 1; factorial n = n * factorial (n - 1)
ghci> factorial 5
120
ghci> :t factorial
factorial :: (Eq p, Num p) => p -> p
Такое разное ветвление
В языке
Условный оператор if
отличается от аналогов в императивных языках тем, что он является выражением, а не управляющей конструкцией.
Это означает, что:
-
-
- обе ветки (
Пример:
Охраняющие (или охранные) выражения (guards)
это удобный и читаемый способ определения функций с несколькими условиями. Они особенно полезны, когда нужно проверить несколько логических условий (например, в зависимости от значений аргументов), и выглядят гораздо элегантнее вложенных
При этом:
- Все ветки (результаты) должны иметь один и тот же тип.
- Если ни одно условие не выполнено и нет
Пример:
Сопоставление с образцом (pattern matching)
одна из самых мощных и характерных черт языка Haskell. Оно позволяет разбирать структуры данных (списки, кортежи, пользовательские типы и т.д.) прямо в определении функций, присваиваниях или ветвлениях, проверяя их форму и извлекая значения.
При этом:
- Порядок важен: сопоставление проверяется сверху вниз.
- Образцы должны быть исчерпывающими: если возможен случай, не охваченный ни одним паттерном, компилятор выдаст предупреждение (или ошибка, если включён
- Нельзя использовать произвольные функции в образцах - только конструкторы данных, константы и переменные.
Пример:
В языке
haskell существует несколько способов поменять линейный ход выполнения программы.Условный оператор if
отличается от аналогов в императивных языках тем, что он является выражением, а не управляющей конструкцией.
Это означает, что:
-
if всегда должен иметь и then, и else;-
if ... then ... else ... возвращает значение, которое можно использовать, например, присвоить переменной или передать в функцию;- обе ветки (
then и else) должны иметь один и тот же тип.Пример:
signum' :: Int -> Int
signum' x = if x > 0
then 1
else if x < 0
then -1
else 0
Охраняющие (или охранные) выражения (guards)
это удобный и читаемый способ определения функций с несколькими условиями. Они особенно полезны, когда нужно проверить несколько логических условий (например, в зависимости от значений аргументов), и выглядят гораздо элегантнее вложенных
if-then-else.При этом:
- Все ветки (результаты) должны иметь один и тот же тип.
- Если ни одно условие не выполнено и нет
otherwise - функция завершится исключением (ошибка времени выполнения). Поэтому почти всегда следует добавлять otherwise в конец.Пример:
signum'' :: Int -> Int
signum'' x
| x > 0 = 1
| x < 0 = -1
| otherwise = 0
Сопоставление с образцом (pattern matching)
одна из самых мощных и характерных черт языка Haskell. Оно позволяет разбирать структуры данных (списки, кортежи, пользовательские типы и т.д.) прямо в определении функций, присваиваниях или ветвлениях, проверяя их форму и извлекая значения.
При этом:
- Порядок важен: сопоставление проверяется сверху вниз.
- Образцы должны быть исчерпывающими: если возможен случай, не охваченный ни одним паттерном, компилятор выдаст предупреждение (или ошибка, если включён
-Werror).- Нельзя использовать произвольные функции в образцах - только конструкторы данных, константы и переменные.
Пример:
isEmpty :: [a] -> Bool
isEmpty [] = True
isEmpty _ = False
👍2
Лямбда-исчисление
Это формальная система в математической логике и теоретической информатике, предназначенная для описания вычислений с помощью функций. Оно было разработано в 1930-х годах Алонзо Чёрчем как способ формализовать понятие вычислимой функции и лежит в основе функционального программирования.
Основные идеи
1. Всё - это функция. В лямбда-исчислении нет чисел, строк или других типов данных в привычном смысле. Всё представляется как функции. Даже числа, логические значения и операции над ними кодируются с помощью функций (например, через кодировку Чёрча).
2. Лямбда-выражения - это основные "строительные блоки" системы. Есть три типа выражений:
- Переменные: например, x, y.
- Абстракции (определения функции): записываются как
- Аппликации (применение функции): записывается как
3. β-редукция - основное правило вычисления. Это процесс подстановки аргумента в тело функции:
То есть, функция
4. α-конверсия - переименование связанных переменных. Например,
5. η-эквивалентность - принцип расширяемости. Говорит, что если две функции ведут себя одинаково на всех аргументах, то они эквивалентны:
Значение лямбда-исчисления
Теоретическое:
- Эквивалентно машине Тьюринга по вычислительной мощности (тезис Чёрча–Тьюринга).
- Лежит в основе теории типов, формальных языков и логики.
Практическое:
- Влияние на функциональные языки программирования: Haskell, Lisp, ML, Scheme, Scala, F# и др.
- Концепция анонимных функций (лямбда-выражений) в современных языках (Python, JavaScript, Java и др.) идёт именно отсюда.
Типизированное и нетипизированное λ-исчисление
- Нетипизированное - самый простой и выразительный вариант, но в нём возможны "странные" выражения, не имеющие нормальной формы (например, (λx.x x)(λx.x x) - зацикливается).
- Типизированное - каждое выражение имеет тип, что исключает некоторые виды ошибок и делает систему безопасной (например, простое типизированное λ-исчисление, система F, исчисление конструкций и т.д.).
Это формальная система в математической логике и теоретической информатике, предназначенная для описания вычислений с помощью функций. Оно было разработано в 1930-х годах Алонзо Чёрчем как способ формализовать понятие вычислимой функции и лежит в основе функционального программирования.
Основные идеи
1. Всё - это функция. В лямбда-исчислении нет чисел, строк или других типов данных в привычном смысле. Всё представляется как функции. Даже числа, логические значения и операции над ними кодируются с помощью функций (например, через кодировку Чёрча).
2. Лямбда-выражения - это основные "строительные блоки" системы. Есть три типа выражений:
- Переменные: например, x, y.
- Абстракции (определения функции): записываются как
λx.M, что означает "функция от аргумента x, возвращающая выражение M".- Аппликации (применение функции): записывается как
(M N), что означает "применить функцию M к аргументу N".3. β-редукция - основное правило вычисления. Это процесс подстановки аргумента в тело функции:
(λx.M) N → M[x := N]
То есть, функция
λx.M применяется к N, и все вхождения x в M заменяются на N.4. α-конверсия - переименование связанных переменных. Например,
λx.x и λy.y считаются эквивалентными, потому что имена связанных переменных не важны.5. η-эквивалентность - принцип расширяемости. Говорит, что если две функции ведут себя одинаково на всех аргументах, то они эквивалентны:
λx.(f x) ≡ f, если x не свободен в f
Значение лямбда-исчисления
Теоретическое:
- Эквивалентно машине Тьюринга по вычислительной мощности (тезис Чёрча–Тьюринга).
- Лежит в основе теории типов, формальных языков и логики.
Практическое:
- Влияние на функциональные языки программирования: Haskell, Lisp, ML, Scheme, Scala, F# и др.
- Концепция анонимных функций (лямбда-выражений) в современных языках (Python, JavaScript, Java и др.) идёт именно отсюда.
Типизированное и нетипизированное λ-исчисление
- Нетипизированное - самый простой и выразительный вариант, но в нём возможны "странные" выражения, не имеющие нормальной формы (например, (λx.x x)(λx.x x) - зацикливается).
- Типизированное - каждое выражение имеет тип, что исключает некоторые виды ошибок и делает систему безопасной (например, простое типизированное λ-исчисление, система F, исчисление конструкций и т.д.).
👍1
Лямбда-функции в Haskell
это функция без имени, которая создаётся на месте и может быть передана в качестве аргумента или использована в выражении. В Haskell синтаксис лямбда-функций основан на лямбда-исчислении, где
Haskell — один из самых чистых функциональных языков, и его дизайн во многом основан на лямбда-исчислении, что делает лямбда-функции естественной частью языка.
Лямбда-функции так же являются значениями и при именовании они становятся эквивалентны обычным функциям:
Внутренне Haskell воспринимает все функции как лямбда-выражения.
Синтаксис
-
-
-
-
Когда использовать лямбда-функции?
Рекомендуется использовать, когда:
- Функция короткая и используется однажды.
- Она передаётся в функцию высшего порядка (например, map, filter, foldr).
- Нужно захватить переменные из окружения (замыкание).
Не рекомендуется, если:
- Лямбда-функция слишком сложная.
- Она используется несколько раз — лучше дать ей имя.
- Код становится менее читаемым.
Примеры
это функция без имени, которая создаётся на месте и может быть передана в качестве аргумента или использована в выражении. В Haskell синтаксис лямбда-функций основан на лямбда-исчислении, где
λ обозначается как \. Haskell — один из самых чистых функциональных языков, и его дизайн во многом основан на лямбда-исчислении, что делает лямбда-функции естественной частью языка.
Лямбда-функции так же являются значениями и при именовании они становятся эквивалентны обычным функциям:
-- Лямбда-функция
addOne = \x -> x + 1
-- То же самое как именованная функция
addOne x = x + 1
Внутренне Haskell воспринимает все функции как лямбда-выражения.
Синтаксис
\аргументы -> выражение
-
\ — начало лямбда-выражения (напоминает греческую букву λ).-
аргументы — список параметров функции (через пробел).-
-> — стрелка, обозначающая тело функции.-
выражение — то, что возвращает функция.Когда использовать лямбда-функции?
Рекомендуется использовать, когда:
- Функция короткая и используется однажды.
- Она передаётся в функцию высшего порядка (например, map, filter, foldr).
- Нужно захватить переменные из окружения (замыкание).
Не рекомендуется, если:
- Лямбда-функция слишком сложная.
- Она используется несколько раз — лучше дать ей имя.
- Код становится менее читаемым.
Примеры
Prelude> (\x -> x + 1) 3
4
Prelude> (\x -> x ++ "!") "Hello"
"Hello!"
Prelude> map (\x -> x ^ 2) [1..5]
[1,4,9,16,25]
Prelude> filter (\(a, b) -> a > b) [(1,2), (3,2), (4,4), (5,3)]
[(3,2),(5,3)]
Хаскелл Брукс Карри
американский математик и логик, чьи работы оказали огромное влияние на развитие математической логики, теории вычислений и функционального программирования.
Хотя Карри не был первым, кто открыл идею, названную его именем, именно его систематическая работа привлекла к ней широкое внимание.
Результатами его работы являются:
1. Каррирование (Currying). Это преобразование функции от нескольких аргументов в последовательность функций от одного аргумента. Например, функция f(x, y) преобразуется в g(x)(y). Хотя идея восходит к Мозесу Шёнфинку и Готтлобу Фреге, именно Карри глубоко исследовал её в контексте комбинаторной логики - поэтому процесс получил его имя.
2. Комбинаторная логика. Карри развил комбинаторную логику - систему, в которой функции строятся без переменных, с использованием базовых "комбинаторов" (например, S, K, I). Эта система стала альтернативой лямбда-исчислению и важной вехой в теории вычислимости.
3. Парадокс Карри. Выявил логический парадокс (называемый парадоксом Карри), показывающий, что в некоторых системах логики можно вывести любое утверждение из самоссылающегося высказывания вида:
Это важный вклад в понимание ограничений формальных систем.
Почему язык программирования называется Haskell?
Язык программирования Haskell (созданный в 1990 году) назван именно в честь Хаскелла Карри - как дань уважения его вкладу в основы функционального программирования, особенно:
- лямбда-исчисление,
- каррирование,
- теорию типов.
Интересный факт: сам Карри не работал с компьютерами - он был чистым математиком и логиком. Но его идеи стали фундаментом для функциональных языков, включая Haskell, ML, Lisp и другие.
Хаскелл Карри - один из отцов современной логики и теоретической информатики. Его имя носит:
- Процесс каррирования (currying),
- Язык программирования Haskell,
- Парадокс Карри,
- Единица измерения "карри" в шуточной академической культуре (для "логической выразительности").
американский математик и логик, чьи работы оказали огромное влияние на развитие математической логики, теории вычислений и функционального программирования.
«Logic is the anatomy of thought.»
Хотя Карри не был первым, кто открыл идею, названную его именем, именно его систематическая работа привлекла к ней широкое внимание.
Результатами его работы являются:
1. Каррирование (Currying). Это преобразование функции от нескольких аргументов в последовательность функций от одного аргумента. Например, функция f(x, y) преобразуется в g(x)(y). Хотя идея восходит к Мозесу Шёнфинку и Готтлобу Фреге, именно Карри глубоко исследовал её в контексте комбинаторной логики - поэтому процесс получил его имя.
2. Комбинаторная логика. Карри развил комбинаторную логику - систему, в которой функции строятся без переменных, с использованием базовых "комбинаторов" (например, S, K, I). Эта система стала альтернативой лямбда-исчислению и важной вехой в теории вычислимости.
3. Парадокс Карри. Выявил логический парадокс (называемый парадоксом Карри), показывающий, что в некоторых системах логики можно вывести любое утверждение из самоссылающегося высказывания вида:
«Если это утверждение истинно, то 1 = 2».
Это важный вклад в понимание ограничений формальных систем.
Почему язык программирования называется Haskell?
Язык программирования Haskell (созданный в 1990 году) назван именно в честь Хаскелла Карри - как дань уважения его вкладу в основы функционального программирования, особенно:
- лямбда-исчисление,
- каррирование,
- теорию типов.
Интересный факт: сам Карри не работал с компьютерами - он был чистым математиком и логиком. Но его идеи стали фундаментом для функциональных языков, включая Haskell, ML, Lisp и другие.
Хаскелл Карри - один из отцов современной логики и теоретической информатики. Его имя носит:
- Процесс каррирования (currying),
- Язык программирования Haskell,
- Парадокс Карри,
- Единица измерения "карри" в шуточной академической культуре (для "логической выразительности").
Функции как значения первого класса
Функции в haskell выступают как полноценные значения, которые можно передавать как аргумент в другие функции, возвращать из функций, присваивать переменным.
Функции как аргументы
Передача функций как аргументов позволяет обобщить процесс повторяющихся вычислений.
Допустим у нас есть функция
Если позже нам понадобятся функции ifEvenDouble и ifEvenSquare, то мы будем вынуждены дублировать условие, а когда оно измениться, то переписывать синхронно все эти функции.
НО можно параметризовать функцию
Не стоит забывать, что лямбда-функции - это полноценные функции и их так же можно передавать как значение.
Наиболее частым примером такого использования функций, не только в haskell, но и в других языках, является сортировка пользовательских типов сортировкой из стандартной библиотеки. Например
Функции как результат
В haskell все функции принимают ровно один аргумент и возвращают одно значение - которое может быть другой функцией. Это называется каррированием, и это фундаментальный принцип функционального программирования - если функция принимает несколько аргументов, она на самом деле возвращает функцию на каждом шаге.
Даже в простом примере:
Тип Int -> Int -> Int на самом деле означает Int -> (Int -> Int) — то есть add принимает Int и возвращает функцию Int -> Int.
Функции можно возвращать и явно. Например, можно изменить нашу функцию
Функции в haskell выступают как полноценные значения, которые можно передавать как аргумент в другие функции, возвращать из функций, присваивать переменным.
Функции как аргументы
Передача функций как аргументов позволяет обобщить процесс повторяющихся вычислений.
Допустим у нас есть функция
ifEvenInc, которая увеличивает число n на единицу, если оно чётное, оставляя его неизменным в противном случае:module Main (main) where
ifEvenInc :: Int -> Int
ifEvenInc n = if even n
then n+1
else n
main :: IO ()
main = do
putStrLn $ show $ ifEvenInc 20
Если позже нам понадобятся функции ifEvenDouble и ifEvenSquare, то мы будем вынуждены дублировать условие, а когда оно измениться, то переписывать синхронно все эти функции.
НО можно параметризовать функцию
ifEven..., передав ей в качестве аргумента функцию-модификатор для x.module Main (main) where
ifEven :: (Int -> Int) -> Int -> Int
ifEven func x = if even x
then func x
else x
ifEvenInc :: Int -> Int
ifEvenInc n = ifEven (\x -> x+1) n
main :: IO ()
main = do
putStrLn $ show $ ifEvenInc 20
Не стоит забывать, что лямбда-функции - это полноценные функции и их так же можно передавать как значение.
Наиболее частым примером такого использования функций, не только в haskell, но и в других языках, является сортировка пользовательских типов сортировкой из стандартной библиотеки. Например
sortBy или std::sort (в C++).Функции как результат
В haskell все функции принимают ровно один аргумент и возвращают одно значение - которое может быть другой функцией. Это называется каррированием, и это фундаментальный принцип функционального программирования - если функция принимает несколько аргументов, она на самом деле возвращает функцию на каждом шаге.
Даже в простом примере:
add :: Int -> Int -> Int
add x y = x + y
Тип Int -> Int -> Int на самом деле означает Int -> (Int -> Int) — то есть add принимает Int и возвращает функцию Int -> Int.
Функции можно возвращать и явно. Например, можно изменить нашу функцию
ifEven. Если она принимает четный первый аргумент, то будет возвращена функция, которая увеличивает свой аргумент на 1, иначе - функция, которая увеличивает свой аргумент в 2 раза:module Main (main) where
ifEven :: Int -> (Int -> Int)
ifEven x = if even x
then (\a -> a+1)
else (\b -> b*2)
main :: IO ()
main = do
let func = ifEven 21
putStrLn $ show $ func 3
❤1
Замыкание
Концепция замыкания довольно простая - когда лямбда-функция захватывает некоторое значение из внешней области видимости, то это называется замыкание.
Например:
тут
Замыкания в Haskell захватывают значения, а не ссылки на изменяемую память, и поскольку значения неизменны, разницы между «по значению» и «по ссылке» в привычном смысле не существует. Но стоит отметить, что haskell может делить (share) подвыражения в памяти - то есть, если замыкание захватывает большое значение, оно не обязательно копируется полностью, а может ссылаться на уже существующую структуру в памяти. Но это оптимизация реализации, а не семантика: с точки зрения программы, это всё равно «значение», и оно неизменно.
Концепция замыкания довольно простая - когда лямбда-функция захватывает некоторое значение из внешней области видимости, то это называется замыкание.
Например:
genIfEven f = (\x -> ifEven f x)
тут
f - захватывается лямбда-функцией.Замыкания в Haskell захватывают значения, а не ссылки на изменяемую память, и поскольку значения неизменны, разницы между «по значению» и «по ссылке» в привычном смысле не существует. Но стоит отметить, что haskell может делить (share) подвыражения в памяти - то есть, если замыкание захватывает большое значение, оно не обязательно копируется полностью, а может ссылаться на уже существующую структуру в памяти. Но это оптимизация реализации, а не семантика: с точки зрения программы, это всё равно «значение», и оно неизменно.
