Перейти к основному содержимому

Простые приложения на 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 по краям") делает даже маленькие приложения:

  • проще для тестирования;
  • понятнее при отладке;
  • устойчивее при росте требований.

Именно поэтому учебные примеры ниже полезно читать не как "набор сниппетов", а как архитектурный шаблон.


Как запускать примеры из главы

  1. GHC: ghc имя.hs && ./имя (Linux/macOS) или имя.exe (Windows).
  2. Stack: stack ghc -- имя.hs или проект с stack run.
  3. Нужно: 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" из трёх слоёв:

  1. Домен: тип Task, чистые функции addTask, completeTask, removeTask.
  2. Сериализация: чтение/запись JSON (aeson).
  3. CLI: разбор аргументов и печать результата (IO-слой).

Так вы сразу примените композицию, типы и разделение эффектов в одном проекте.


Частые ошибки в маленьких приложениях

  • слишком раннее смешивание бизнес-логики и IO;
  • частичные функции (head, read) без безопасной обработки;
  • отсутствие явных сигнатур у публичных функций;
  • хранение "ошибок строками" вместо Either.

Что читать дальше


См. также — Основы функционального программирования, Типы данных и система типов, Функции, каррирование и композиция, Архитектура выполнения Haskell-программ.