Как я подхожу к дизайну API библиотеки на Си
В данной заметке я поделюсь своим видением идеальной библиотеки на Си. Будет предложена простая и понятная организация файлов и директорий в дистрибутиве, которую можно взять за основу, а также небольшие guidelines по общему дизайну современного API на языке Си.
Перехват malloc()/realloc()/free() для отладки утечек памяти
Перехватывая функции управления памятью в Си, можно хранить полезную информацию о контексте вызова функции, которая хорошо помогает отслеживать утечки памяти.
Базовая работа с GDAL: гипсобатиметрическая модель Земли
Подготовим простенькую C++ модель, возвращающую глубину Мирового океана или высоту над уровнем моря для заданных географических координат (широта, долгота), а также строгую принадлежность данной точки суше либо океану.
Реализация паттерна проектирования Observer на С++17
Идея Паттерн проектирования Observer (наблюдатель) является классическим и элегантным маханизмом динамического оповещения одних объектов об обновлениях состояния других объектов. В нём один или несколько объектов-наблюдателей неким образом подключается к объекту-наблюдаемому, будто-бы подписываясь на его обновления. Наблюдаемое же ничего не знает о своих подписчиках и вызывает их общий метод, когда посчитает нужным, обычно передавая в качестве параметра указатель на самого себя (т.е. на объект-наблюдатель). Таким образом подключенные конкретные наблюдатели асинхронно получают управление и манипулируют конкретным наблюдаемым, полученным во входном параметре.
Трансформация части контейнера с std::transform()
std::transform() Стандартная библиотека C++ STL содержит множество обобщённых и интересных инструментов для работы с контейнерами, что не удивительно, ведь одна из главных причин появления STL – это желание элегантно абстрагировать алгоритмы и отделить их от данных, т.е. от этих самых контейнеров.
Один из простых и довольно интуитивных (для функциональных программистов) алгоритмов – трансформация последовательной области контейнера. В двух словах состит он в следующем: применяем функцию (оператор) к одному элементу контейнера, пишем куда-нибудь результат (новое значение элемента), затем передвигаем итераторы обоих контейнеров вперёд и повторяем весь процесс снова.
Сигналы и слоты: асинхронные, тип-безопасные коллбэки с libsigc++
Введение Концепция сигналов/слотов (Signals/Slots), т.е. асинхронных коллбэков (callback) широко применяется в различных программных архитектурах: от системного уровня до высокоуровневых многокомпонентных библиотек вроде GUI (например, QT) или игровых движков.
В общем виде идея заключается в следующем:
Объекты A и B работают параллельно. Объект A регистрирует свой некоторый метод или анонимную функцию (callback-код) у объекта B. В момент регистрации объект A может передать также данные, которые должны быть доступны callback-коду. Объекты продолжают работу параллельно (без специальной взаимосвязи либо синхронизации).
C++: горизонтальный полиморфизм с dynamic_cast
Введение Предположим, что мы разрабатываем приложение для военной симуляции, в котором имеется поддержка разнообразных по своим возможностям и характеристикам боевых платформ. В дизайне нашей системы мы решили отказаться от громоздкой иерархии наследования в пользу набора независимых друг от друга чистых виртуальных классов (интерфейсов), подмножество которых реализуется конкрециями через множественное наследование C++ (которое очень даже безопасно и применимо в случаях с несвязанными интерфейсами).
C++ предоставляет оператор dynamic_cast<T>() для безопасного перехода (т.е. преобразования указателя) между интерфейсами, имплементированными данной конкрецией.
Простая сериализация/десериализация полиморфических данных в C++
Введение Предположим, что мы, как это часто бывает, имеем вектор интерфейсов к конкретным объектам:
WidgetClassA w1; WidgetClassB w2; std::vector<WidgetInterface*> wifv {&w1, &w2, ...}; /* * Прямое манипулирование конкрециями */ w1.setWidgetAParam("Test"); w2.setWidgetBSize( w1.getWidgetASize() ); // ... /* * Манипулирование разными виджетами через абстрактный интерфейс виджетов */ for (WidgetInterface* wif : wifv) { wif->widgetDoSomething(); size_t i = wif->getSize(); // ... } Мы хотим (де)сериализовать все объекты, на которые указывают интерфейсы (чистые виртуальные классы) в векторе wifv, т.
Простая и комфортная разработка на C++ в vim
Вкратце В данной заметке я приведу свой текущий ~/.vimrc, настраивающий как общее поведение самого редактора vim/gvim (для редактирования любого текста), так и дополнительный функционал для разработки программ на C/C++. Сам по себе vim “из коробки” отлично подходит для редактирования кода, однако несколькими дополнительными модулями из данного текстового редактора можно сделать мощнейшую среду разработки (IDE) промышленного класса. Менеджер модулей vim-plug автоматически скачивает и обновляет плагины, поэтому для воспроизведения всей моей IDE фактически потребуется лишь один файл ~/.
Простая и удобная среда разработки Haskell-кода в vim
Текстовый редактор vim/nvim очень хорош!
Разумеется, для такого популярного редактора существует множество модулей, существенно помогающих в разработке программного кода на бесчисленных языках программирования.
В данной короткой заметке я набросаю простой и быстрый вариант рабочего окружения (нужные модули для vim, их настройка, внешние программы) для комфортного программирования на Haskell в текстовом редакторе vim или nvim (все настройки ниже должны быть полностью совместимы как с классическим vim версии 8 и выше, так и с neovim).
rea: Библиотека для декларативного описания стратегических игр
После прочтения довольно любопытной исследовательской работы “Resource Entity Action: A Generalized Design Pattern for RTS games” я решил реализовать предложенную концепцию (в несколько упрощённом виде) на Haskell. Ниже пойдёт речь об идее подхода Resource-Entity-Action к проектированию RTS-игр (Command & Conquer, Age of Empires, StarCraft, …) и прочих симуляций, а также о том, как использовать библиотеку rea.
Подход Resource-Entity-Action Предложенная в статье выше идея довольно проста по своей сути: для описания логики некоторой RTS-игры мы используем трансферы абстрактных ресурсов между сущностями.
Функциональное реактивное программирование GTK+ и Cairo: программа-рисовалка
В данной заметке мы покажем, как функциональное реактивное программирование (FRP) позволяет нам полностью декларативно описать механику графической программы и чрезвычайно упростить её код. В качестве иллюстрации мы напишем незамысловатую программу-рисовалку, которая использует (обыкновенные) императивные биндинги к GTK и Cairo и пользовательский интерфейс которой создан в UI-редакторе Glade.
Для FRP мы будем использовать замечательный пакет reactive-banana. Мостик между GTK и reactive-banana заполнит библиотечка reactive-banana-gi-gtk.
Подготовка Зависимости нашей программы:
text mtl reactive-banana reactive-banana-gi-gtk gi-gtk gi-gdk gi-gobject gi-cairo cairo Этапы построения программы Мы можем разделить процесс создания программы на следующие этапы:
Процесс создания собственных декларативных GTK+ виджетов
Процесс создания собственных декларативных виджетов в библиотеке gi-gtk-declarative не так уж и сложен. Его можно разбить на следующие шаги:
Измышление механики будущего виджета, понимание нижележащих GTK-виджетов, его составляющих Описание типа меняемых пользователем параметров (properties) виджета - для установления чистой функциональной зависимости от структуры пользователя. Описание типа всех событий (events), которые будет генерировать виджет (например, в ответ на срабатывания сигналов внутренних GTK-виджетов или достижения определённых состояний автомата логики) Описание внутреннего стэйта нашего виджета (например, поддерживание набора составляющих низкоуровневых GTK-виджетов для доступа к их состояниям) Создание пяти функций, необходимых для конструирования значения типа CustomWidget widget customData event (что в свою очередь даст нам искомый Widget event): data CustomWidget widget customData event = CustomWidget { customWidget :: Gtk.
gi-gtk-declarative: Полностью декларативные GTK-программы на Haskell
Императивный GTK Программирование графических (GUI) приложений популярным императивным способом возможно и на Haskell: при таком подходе мы обычно работаем в монаде IO (или в параметризованном IO трансформере, например StateT) и логика программы частенько полностью идентична императивным языкам. Например, программа ниже использует библиотеку GTK3 в императивном стиле (низкоуровневые биндинги к GTK3 пакетом gi-gtk):
import qualified GI.Gtk as Gtk import Data.GI.Base main :: IO () main = do Gtk.init Nothing win <- new Gtk.
Монада RWS: Reader + Writer + State в одном интерфейсе
Фундаментальные трансформеры монад ReaderT (read-only контекст: например, настройки программы из конфигурационного файла), WriterT (write-only контекст: например, лог работы программы) и StateT (read/write контекст: например, счётчики и прочее внутреннее состояние логики программы) стандартных библиотек mtl и transformers очень употребительны. Их можно композиционно параметризовать любой “объемлющей” монадой для моделирования требуемого вычислительного контекста (например, WriterT w IO для программы, которая общается по сети и протоколирует (логирует) свою работу моноидом r). Трансформеры легко параметризуются друг-другом; мы работает с вложенными в “стопку” монадами путём лифтинга комбинаторов к нужным монадам с помощью функций семейства lift.
Аппликативный комонадический (анти)контекст: ComonadApply
Типы Предположим, что мы программируем некую компьютерную симуляцию и решили выделить следующие типы в нашей программе:
Тип, моделирующий внутреннее состояние сущности (допустим, юнита в компьютерной игрей): data Unit = Unit { _unitName :: String , _unitPosition :: L.V2 Double } Тип, моделирующий более абстрактную, внешнюю (например, административную) информацию, присущую вообще всем объектам симуляции: data Object s = forall d . Object { _objectId :: Int , _objectState :: s , _objectIntelDB :: [d] -- , .
Свободная монада для функтора или изоляция сайд-эффектов в программе
На основе англоязычной статьи Gabriel Gonzalez.
Предположим, что мы написали простейшую компьютерную программу, делающую некий ввод-вывод (т.е. имеющую сайд-эффекты):
module Main where import Data.Char (toUpper) import System.Exit (exitSuccess) main = do x <- getLine putStrLn (map toUpper x) putStrLn "Finished" exitSuccess Всё бы ничего, но данный код смешивает “чистую” логику (не имеющую сторонних эффектов чистую трансформацию строки в верхний регистр: строка -> СТРОКА) и “грязный” ввод-вывод с терминала (имеющий сторонние эффекты вроде ожидания клавиатурного ввода, разрыв терминальной линии и т.
Эффективный, оптимальный, типизированный diff между данными произвольного типа
Глубокие теоретико-математические основания и лаконичный синтаксис Haskell делают этот язык одним из наиболее совершенных средств осмысления и составления компьютерных программ за всю историю кибернетики.
В данной короткой заметке мы воспользуемся пакетами для Обобщённого Программирования (Generic Programming) и покажем, как можно получать разницу (diff) между данными некоторого типа, а также накладывать эту разницу (patch) для получения эндоморфизма типа.
Задача Положим, мы имеем следующий тип автомобилей:
data Color = White | Black data Car = Car { carMaker :: String , carColor :: Color , carPrice :: Int } Наша задача состоит в получении разницы (диффа, дельты) между двумя автомобилями, белым “Volvo” и чёрным “BMW”:
Haskell: Применение паттерна MVC для программ с GTK+
Введение В данной заметке мы исследуем один из способов структурирования на Haskell графических программ (GTK3) архитектурным паттерном Model-View-Controller.
Для этих целей мы будем применять пакет mvc и Haskell-биндинги к GTK3 gi-gtk. Поскольку поток выполнения любой GTK+ программы обычно (после инициализации, настройки коллбэков, и т.д.) рано или поздно входит в бесконечный библиотечный цикл gtk_main(), нам придётся что-то придумать для одновременного применения графической подсистемы вместе с пакетом mvc.
Любой GUI является лишь частью некоторой программы, но не наоборот, поэтому мы считаем запуск и выполнение MVC более общим и первичным, а GTK+ – вторичным и потому запускаемым и выполняемым “внутри” нашей архитектуры MVC.
mvc: архитектурный паттерн Model-View-Controller в Haskell
Для Haskell существует замечательная библиотека mvc, реализующая архитектуру Model-View-Controller для проектирования многопоточных программ. Библиотека построена на базе поточных процессоров pipes, async и других известных абстракций.
![/img/500px-MVC-Process.svg.png)
В данной заметке мы познакомимся с mvc и спроектируем простенькую графическую программу на SDL.
Компоненты mvc Библиотека mvc преднамеренно форсирует нас расщеплять дизайн программы на три фундаментальных компонента:
Тип Controller, снабжающий ядро программы некоторыми входными данными (стимулами) из внешнего мира. Моделируется “нечистыми” вычислениями, т.
Почему конструкция if-then-else малополезна в языках программирования
Введение: if-then-else В большинстве языков программирования имеется условная конструкция IF ... THEN ... ELSE .... Она знакома любому школьнику и представляет собой развилку на пути из некоторого абстрактного типа a в тип b. Решение о том, взять первый путь или второй, принимается с помощью предиката, то есть путя из a в булев тип (истина/ложь). Вместе с обоими вариантами b (т.н. ветками условия), предикат также идёт параметром к конструкции IF ... THEN .
Генерация MathJax в Hakyll: написание математических формул в markdown
Введение Hakyll - отличная система генерации статичных вэб-сайтов (блогов, персональных страничек, …), написанная на языке Haskell. Фактически же, hakyll - это всего-навсего набор библиотек для сборки пользователем собственной Haskell-программы, которая и используется в дальнейшем для создания (генерации, обновления) статичных (HTML) вэб-страничек с использованием шаблонов.
На вход программе-генератору обычно поступают обыкновенные текстовые файлы в синтаксисе Markdown, в которых и содержится наполнение страниц сайта. Hakyll использует мощнейшую Haskell-библиотеку pandoc для преобразования информации из Markdown в HTML и многие другие форматы.
Простая интуиция линз, призм, изоморфизмов (lens)
Введение В данной заметке я вкратце и упрощённо поделюсь своим пониманием общей картины (“Big Picture”) библиотеки lens и вытекающей из него интуиции о линзах.
Интуиция основных операций с линзовыми объектами Итак, имея линзовый объект (о них - позже), мы можем осуществлять с его помощью четыре фундаментальных действия:
Смотреть на фокус линзы, то есть фокусироваться внутрь структуры данных: view Модифицировать данные в фокусе линзы, оставляя остальную структуру без изменений: over Полностью замещать данные в фокусе линзы (может быть, на другой тип), оставляя остальную структуру без изменений: set Комбинировать линзовые объекты друг с другом, производя новый линзовый объект: (.
Траверсабельные и битраверсабельные структуры: Data.Traversable
Интуиция тайпкласса Traversable Тайпкласс Data.Traversable базируется на функториальных, фолдабельных абстракциях и представляет собой такие (часто контейнерные) структуры данных, которые можно последовательно погрузить в некоторый аппликативный контекст с сохранением формы структуры.
Проще говоря, траверсабельные контейнеры позволяют замапить все элементы структуры в вычислительный контекст, произвести эти вычисления слева-направо и собрать результат в контексте внутри точно такого-же контейнера.
Если Data.Foldable подразумевает полное разрушение формы контейнера в процессе последовательной свёртки, т. е. создания дайджеста, то Data.
Два пути к Data.Foldable: структуры, допускающие свёртку
Интуиция Класс Data.Foldable представляет собой абстракцию над такими (часто контейнерными) типами данных, которые допускают свёртку (fold). Для этих типов осмыслена и определена операция коллапса, то есть разрушения и схлопывания всей структуры в одно единственное итоговое значение (дайджест, резюме).
> foldr (+) 100 $ Node 2 [Node 1 [], Node 3 []] 106 > let f = foldr (.) id [(+1), (*2), (+10)] > :t f f :: Num b => b -> b > f 1 23 Если некоторый тип t реализует интерфейс тайпкласса Data.
Функторы как адаптеры между категориями
Функторы в теории категорий Как известно, функтор в теории категорий определён как морфизм между двумя категориями (маппинг всех объектов одной категории в объекты другой, маппинг всех морфизмов одной категории в морфизмы другой), который сохраняет нейтральные элементы (Identity) и композицию в соответствующих категориях.
Функтор, таким образом, – более общий случай понятия гомоморфизма (групп, моноидов и т.п.), то есть отображения, сохраняющего (алгебраическую) структуру.
Функторы в философии В отличии от Множества, которое по своей природе лишено всякой структуры, любая Категория – это сама структура в чистом виде (и ничего кроме структуры).
intero-mode: быстрая настройка Emacs для комфортной работы с Haskell-кодом
Не секрет, что хакеры предпочитают использовать текстовые редакторы emacs или vim. Индивидуальные настройки редактора, скрипты и т.п. при этом могут жить десятилетиями, поскольку являются делом профессиональной привычки.
Для редактирования Haskell-кода в emacs традиционно были пакеты haskell-mode и ghc-mod, однако не так давно появился замечательный проект intero-mode, предлагающий хороший функционал “из коробки” и настраивать среду для комфортной работы стало быстрее и проще.
Установка intero-mode После добавления MELPA-репозитория пакетов к emacs, установить intero-mode можно, найдя свежую версию “intero” в M-x package-list-packages.
OpenGL в Haskell: загрузка текстуры в GPU, UV-координаты
На базе нашей программки вращения кубика (из прошлых статей) разберёмся с простейшей загрузкой текстур в аппаратуру и текстурированием четырёхугольника. Забудем пока про куб и цвета и вернёмся к квадрату со стороной 1 и 2D-координатам.
UV-координаты В компьютерной графике для “закрепления” текстуры на объекте используют так называемые UV-координаты.
Размеры текстуры нормализуются до [0..1] по оси X и Y, и каждая вершина треугольников, из которых состоит объект, мапится к пикселю картинки как показано выше.
Объекты OpenGL в Haskell: VAO и VBO
В прошлый раз мы говорили об инициализации системы OpenGL на Haskell и создали простенький модульный шаблон программы (мы используем биндинги GLUT).
В этот раз мы немного дополним наш кубик, раскрасив вершины треугольников, из которых он собран, случайными цветами.
Немного о VAO и VBO Спецификация OpenGL определяет два объекта, которые мы уже использовали:
VAO (Vertex Array Object): инкапсулирует состояние системы, достаточное для запуска конвейера (то есть для поставки вершинных данных) и полного рендеринга объекта.
Инициализация программируемого графического конвейера OpenGL
Для Haskell есть замечательные биндинги к OpenGL, а также биндинги к популярным “клиентским” вспомогательным библиотекам вроде GLUT, GLFW, SDL и прочим.
Мощь и изящество языка Haskell вместе с разнообразием фундаментальных подходов к композиции программ (монады, Arrow, FRP и т.д.) делают его чуть-ли не идеальным инструментом в написании корректных графических программ вроде симуляций или игр.
Задачи Попробуем разобраться с минимальными действиями, необходимыми для создания простенькой программки, использующей систему OpenGL современным образом: а именно, выполняя вычисления на высокопроизводительном GPU и управляя программируемым графическим конвейером с помощью вершинных, фрагментных и других шейдеров.
Монада Identity или почему Haskell - самый лучший императивный язык программирования
Введение Предположим, что у нас есть целая библиотека архи-полезных функций:
timesTwo :: Int -> Int timesTwo = (* 2) timesTen :: Int -> Int timesTen = (* 5) . timesTwo isBig :: Int -> Bool isBig = (>= 100) countChars :: String -> Int countChars = length Мы составили (популярную) компьютерную программу на базе этой библиотеки (точка . – операция композиции функций):
program = isBig . timesTen . countChars Программа принимает на вход строку, а выдаёт значение “истина/ложь”.
Контравариантные функторы, бифункторы, профункторы в Haskell
Введение Предположим, что у нас есть следующая структура данных, моделирующая некоторое абстрактное преобразование сущности i в сущность o:
newtype ConversionFromTo i o = ConverterFromTo { convertFromTo :: i -> o } Типы i и o мыслятся входом и выходом соответственно: функция convertFromTo просто запускает описанное в ConversionFromTo i o преобразование над аргументом типа i. Обратим внимание, что последовательность аргументов важна, и первым параметром к конструктору значения типа ConversionFromTo i o идёт тип входного сигнала.
На пути к пониманию стрелок (Arrow): функции с памятью
Стрелки (тайпклассы и комбинаторы из Control.Arrow) являются замечательным обобщением понятия функции. Стрелка y a b представляет собой некоторый процесс, принимающий значения типа a и выдающий значения типа b.
Помимо прочих мест, довольно подробная статья про стрелки есть на англоязычном Викиучебнике. Ниже я попробую пересказать её часть своими словами и подробно объяснить некоторые моменты (отсутствующие в Вики), которые сам понял не сразу.
Стрелочных комбинаторов >>>, first, &&& и прочих я коснусь в отдельной статье.
Простое объяснение идеи монад за 30 секунд
Композиция Нестрого заметим, что любая компьютерная программа представляет собой некий абстрактный вычислительный блок. Вычислительный блок просто соединяет вход программы с её выходом (результатом).
Довольно сложно проектировать и поддерживать большие программы, состоящие из одного громадного вычислительного блока. Искусство програмирования, поэтому, есть искусство разбиения большого вычислительного блока на несколько малых блоков. Каждый блок при этом призван решить свою собственную подпроблему (подзадачу). Так достигается модульность решения задачи.
Раньше у нас был один единственный вычислительный блок, а теперь – несколько маленьких блоков.
Линзы в Haskell: эквивалентности (Equality)
В прошлой записи мы в общих чертах ознакомились с замечательным пакетом lens, реализующим технику функционального программирования, известную как линзы (обобщённые геттеры и сеттеры).
В пакете определена целая иерархия “разновидностей” линз, откуда и разные названия для их типов: Equality, Iso, Lens, Prism, Getter, Traversal и т.д. Комбинаторы с верхних уровней следующей диаграммы можно применять к типам на нижележащих уровнях:
Чтобы лучше овладеть пакетом lens, на протяжении нескольких записей я попробую вкратце сформулировать особенности и цели использования, а также привести содержательные примеры для каждого из типов с диаграммы выше.
Линзы и призмы в Haskell
Введение Линзы – это функциональные композиционабельные проекции типов, сильно облегчающие доступ к многоуровневым, вложенным структурам данных. Под “проекцией типа” я здесь имею в виду некоторый тип T, конструируемый N-арным конструктором значений makeT, вместе с некоторой функцией proj_N, деконструирующей значение T в его N-ый элемент:
data T = makeT A B C D proj_A :: T -> A proj_B :: T -> B proj_C :: T -> C proj_D :: T -> D т.
Реализация Random для перечисляемого и ограниченного типа
Предположим, что у нас есть следующий тип, описывающий вид дерева (дуб, сосна, берёза, …):
data TreeType = Pine | Oak | Birch | Maple Мы бы хотели уметь строить дерево случайного вида, т.е. использовать интерфейс тайпкласса Random и комбинаторы из System.Random:
Тайпкласс Random Тайпкласс случайных величин Random в Haskell имеет следующий интерфейс:
randomR :: (RandomGen g, Random a) => (a, a) -> g -> (a, g) random :: (RandomGen g, Random a) => g -> (a, g) Обе функции принимают на вход детерминированный генератор псевдослучайных чисел, а функция randomR также - мнимальное и максимальное значение случайной величины, в интервале между которыми и будет сделан случайный выбор.
Простейший (ан)парсинг типов Haskell в JSON c Data.Aeson
Введение Сериализация и десериализация типов Haskell в популярный формат JSON проста как пробка с пакетом Aeson (Data.Aeson).
Положим, что T - тип данных, который мы хотим сохранять в и считывать из формата JSON. Для этого нам нужны стрелки (изоморфизм) между типами T и ByteString, строками, содержащими все корректные JSON-репрезентации значений нашего T.
Пакет Data.Aeson содержит высокоэффективные функции работы с форматом JSON. Среди прочих имеем функции кодирования и декодирования между JSON и типами Haskell:
Частичные функции и композиция Клейсли
Частичными называют такие функции, которые не определены для некоторых значений своего аргумента. Например, отображение “рост_в_сантиметрах” из множества людей в множество натуральных чисел определено на всей области определения (каждому человеку поставлено в соответствие число), тогда как отображение “зарплата” определено не для каждого человека (кто-то не работает).
Компьютерная программа – это композиция (то есть функция от функции от … от функции аргумента) некоторых “функциональных блоков”. Например, программа-просмотрщик картинок вроде geeqie является удачной композицией функций из графической библиотеки GTK, библиотек декодирования изображений JPEG, PNG, стандартных библиотек языка Си и т.
Идея: обобщённый нотариус на смарт-контрактах
В процессе знакомства с замечательной, поистине революционной технологией “всемирного компьютера” Ethereum и языка описания смарт-контрактов Solidity, у меня возникла следующая скромная идея.
А что если (в отличии от не менее интересных электронных нотариальных сервисов) трактовать само понятие “нотариуса” (уполномоченной свидетельствовать верность документов сущности) более общо?
Обычный нотариус Например, традиционный нотариус, заверяющий ксерокопию паспорта или свидетельство о браке, по сути есть не что иное, как функция:
notarify :: Document -> Proof -> Fee -> (Document, Signature) То есть пафосная тётя из нотариальной конторы фактически принимает от клиента:
Десериализация в Haskell: парсинг карт TacOps
Введение Предположим, мы читаем двоичную последовательность из файла (или слушаем на проводе) и хотим преобразовать её в некоторую осмысленную структуру. Для этого нам нужен парсер – вычислительный процесс, который, грубо говоря, разобъёт бездушную последовательность единиц и нулей на несущие смысл компоненты и сконструирует из них нашу структуру.
И наоборот, мы хотим сохранить известную структуру данных на диск (например, для последующего архивирования) или передать по сети другому компьютеру. Нам нужно провести обратную операцию – анпарсинг, т.
Монада инвестиции: простой язык описания и симуляции инвестиций
Можно просто посмотреть пример.
Краткое описание Накатал забавную Haskell-библиотечку для моделирования финансового поведения: инвестиций, личных трат, выплат, торговли на бирже, и прочих алгоритмов продолжительных и условных накоплений и передвижений средств. Смысл простой: тип Investment является синонимом специализации трансформера монад StateT на монаду IO:
-- | The investment monad type type Investment a r = StateT (InvestmentState a) IO r При этом тип InvestmentState, описывающий текущее состояние инвестиции (или чьего-то кошелька и долгов…), определён так:
Моделирование валюты в Haskell
Любопытная задачка – смоделировать валюту, т.е. конвертабельные друг в друга денежные средства. Чтобы уметь делать утверждения о валюте, хранить и конвертировать её, нам нужно определить тип для представления денег в программе.
В первом приближении можно рассуждать так:
data Symbol = USD | RUB | BTC | LTC | ETH | DASH | ZEC type Money = (Symbol, Double) Но при таком подходе типы выражений (USD, 100) (купюра в сто баксов) и (RUB, 1) будут одинаковы, а значит любые комбинаторы над типом Money каким-то образом должны будут учитывать разные денежные знаки (разные конструкторы типа Symbol).
Haskell: F-алгебры, катаморфизмы и foldr
Мой перевод статьи Bartosz Milewski “Understanding F-Algebras” на schoolofhaskell.
Что такое алгебра? Говоря наивно, алебра – это такая штука, которая даёт нам возможность производить вычисления с числами и символами. Абстрактная алгебра представляет символы как элементы векторного пространства: они могут умножаться на скаляры и складываться друг с другом. Но что отделяет алгебры от линейных пространств, так это наличие векторного умножения: билинейного произведения векторов, рузультатом которого является другой вектор (против скалярного произведения, которое даёт скаляр).
Прогулки с тайпклассами Haskell: аппликативные функторы
Как мы уже выяснили, функторы позволяют втягивать “обычную” функцию в некоторый вычислительный контекст, превращая её в функцию над контекстами:
> :t (* 3) (* 3) :: Num a => a -> a > :t fmap (* 3) fmap (* 3) :: (Functor f, Num b) => f b -> f b В этом примере (* 3) – это функция “утроения” (функция от одного числа, отсюда и a -> a). Функция fmap (* 3) тогда есть “функция утроения содержимого некоторого контейнера (функтора)”.
Тайпклассы Haskell: функториальные законы
В продолжении разговора о функторах стандартной библиотеки Haskell, подумаем над их законами.
Функториальные законы Все реализации Functor должны подчиняться следующим двум законам:
fmap id = id fmap (g . h) = (fmap g) . (fmap h) Первый закон очень ёмок, несмотря на кажущуюся простоту. Он говорит нам о том, что втягивание функции тождества (id a = a) в вычислительный контекст не может изменять контекст. Второй закон говорит нам о дистрибутивности fmap относительно композиции: применение одной функции внутри контекста вслед за другой – это то же самое, что применение композиции этих функций внутри контекста.
Прогулки с тайпклассами Haskell: функторы
Приглашаю всех любознательных новичков, как и я, на прогулку по классам^[В данном случае термины класс и экземляр, разумеется, не имеют ничего общего с созвучными терминами из объектно-ориентированного программирования.] типов стандартной библиотеки языка Haskell. На wiki сообщества хакеров Haskell есть страница Typeclassopedia, в которой подробно рассказывается про самые фундаментальные классы языка, приводятся интересные примеры интуиции (метафор, которыми можно понимать для себя тот или иной класс), а также даются полезные упражнения.
Я бы хотел пройтись по каждому из описанных в Typeclassopedia классов и понять их все; потратить несколько минут благодарных размышлений над красивейшими абстракциями и глубокими математическими идеями, стоящими за каждым из них, а также выполнить все упражнения из параграфов Exercises.