Простые приложения на Haskell
Простые приложения на Haskell
В Haskell бизнес-логику пишут как чистые функции; IO — на границе программы (файлы, сеть). Сборка: cabal или stack; JSON — aeson.
Где Haskell применяют
Язык редко входит в "топ по популярности", но стабильно используется там, где важны корректность, поддерживаемость и строгие контракты типов:
| Область | Примеры |
|---|---|
| Финансы и риск | модели деривативов, внутренние системы банков (в т.ч. Standard Chartered) |
| Компиляторы и верификация | Agda, Epigram, исторический Pugs (Perl 6); формально верифицируемое микроядро seL4 |
| EDA / железо | Bluespec (расширение Haskell), Clash для FPGA |
| Криптография | Cryptol |
| Инфраструктура | парсеры (модуль SQL в SQreamDB), статический анализ (Meta и др.) |
| Инструменты | Pandoc, редактор Yi, IDE Leksah, тайловый WM xmonad, VCS Darcs |
Большинство библиотек на Hackage распространяются под либеральными лицензиями (BSD, MIT, public domain). Сообщество традиционно публикует переиспользуемые пакеты с документацией и тестами.
Почему это важно на практике
Этот подход ("чистое ядро + IO по краям") делает даже маленькие приложения:
- проще для тестирования;
- понятнее при отладке;
- устойчивее при росте требований.
Именно поэтому учебные примеры ниже полезно читать не как "набор сниппетов", а как архитектурный шаблон.
Как запускать примеры из главы
- GHC:
ghc имя.hs && ./имя(Linux/macOS) илиимя.exe(Windows). - Stack:
stack ghc -- имя.hsили проект сstack run. - Нужно: GHC 9+ (
ghc --version).
"Генератор паролей" (чистая часть + IO)
Как запустить
- Файл:
password.hs - Команда:
runhaskell password.hsилиghc -o password password.hs && ./password
Код ITЗагрузка примера кода…
Что здесь можно улучшить в учебных целях:
- добавить проверку минимальной длины пароля;
- вынести выбор набора символов в параметр;
- заменить "сырой" генератор на криптографически стойкий (для реальных систем).
Сортировка строк (чисто)
sortLines :: String -> String
sortLines input =
unlines . sort . filter (not . null) . map (filter (not . isSpace)) . lines $ input
where sort = Data.List.sort
Вызов из main: interact sortLines для stdin/stdout.
Это хороший пример того, как в Haskell обрабатывают поток данных без явных циклов и изменяемых буферов.
Калькулятор с Maybe
calc :: Double -> Double -> Char -> Maybe Double
calc a b '+' = Just (a + b)
calc a b '-' = Just (a - b)
calc a b '*' = Just (a * b)
calc a b '/' | b /= 0 = Just (a / b)
calc _ _ '/' = Nothing
calc _ _ _ = Nothing
Для более информативных ошибок замените Maybe на Either String Double, чтобы различать "неизвестная операция" и "деление на ноль".
Трекер задач (aeson)
{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics
import Data.Aeson
import qualified Data.ByteString.Lazy as B
data Task = Task { taskId :: Int, title :: String, done :: Bool }
deriving (Generic, Show, ToJSON, FromJSON)
loadTasks :: FilePath -> IO [Task]
loadTasks path = do
e <- B.readFile path
pure $ either (const []) id (eitherDecode e)
Мини-расширение к этому примеру:
- добавить
saveTasks :: FilePath -> [Task] -> IO ()черезencode; - добавить команду "пометить задачу выполненной";
- хранить и загружать данные в
tasks.json.
Характерный пример — композиция без циклов
wordCount :: String -> [(String, Int)]
wordCount = map (\w -> (w, length w)) . words
main :: IO ()
main = print $ wordCount "haskell teaches composition"
map, ., words — идиома вместо явных циклов.
Мини-проект для закрепления
Соберите "консольный TODO" из трёх слоёв:
- Домен: тип
Task, чистые функцииaddTask,completeTask,removeTask. - Сериализация: чтение/запись JSON (
aeson). - CLI: разбор аргументов и печать результата (
IO-слой).
Так вы сразу примените композицию, типы и разделение эффектов в одном проекте.
Частые ошибки в маленьких приложениях
- слишком раннее смешивание бизнес-логики и
IO; - частичные функции (
head,read) без безопасной обработки; - отсутствие явных сигнатур у публичных функций;
- хранение "ошибок строками" вместо
Either.
Что читать дальше
- Для архитектурного обоснования разделения "чистое ядро + эффекты": Архитектура выполнения Haskell-программ.
- Для усиления модели типов в прикладном коде: Типы данных и система типов в Haskell.
- Для полировки функционального стиля: Функции, каррирование и композиция.
См. также — Основы функционального программирования, Типы данных и система типов, Функции, каррирование и композиция, Архитектура выполнения Haskell-программ.