- PVSM.RU - https://www.pvsm.ru -
Рекомендуется прочитать первую статью [1], если вы еще этого не сделали. Эта статья будет покороче, меньше сконцентрирована на деталях и больше — на возможностях.
Согласно Стивену Дилю [2], наряду с зависимыми типами, ускорением компиляции и уменьшением порогом вхождения; алгебраические эффекты являются одной из самых главных задач, которые будут решены в будущем для Haskell.
Будущее не за горами, поэтому приступать нужно уже сейчас.
class Liftable eff schema where
lift :: eff ~> schema
Что означает «поднятие» по сути? lift — это тот же самый pure/return за тем тот исключением, что мы погружаем не значение в эффект, а эффект в какой-нибудь трансформер (в нашем случае — в трансформерную схему):
pure :: a -> t a
lift :: u a -> t u a
Это позволяет нам использовать любые эффекты внутри определенного трансформера — вводить новые эффекты просто, но позже нам надо будет проинтерпретировать каждый из них.
Таким образом, мы можем спокойно скомпозировать поднятые эффекты:
let f = lift get :: Configured _ t => t _
let g = lift Nothing :: Optional t => t _
let h = lift (failure _) :: Failable _ t => t _
let x = f *> g *> h :: (Applicative t, Configured _ t, Optional t, Failable _ t) => t _
И представить их в любом удобном для нас порядке:
let y = pure _ :: Reader _ :> State _ :> Either _ :> Maybe := Int
let z = pure _ :: State _ :> Either _ :> Maybe _ :> Reader := _
let x = f *> g *> h :: (Applicative t, Configured _ t, Optional t, Failable _ t) => t _
let xy = x *> y :: Reader _ :> State _ :> Either _ :> Maybe := _
let xz = x *> z :: State _ :> Either _ :> Maybe _ :> Reader := _
class Adaptable subeff eff | subeff -> eff where
adapt :: subeff ~> eff
Адаптация означает, что некоторые эффекты могут быть заменены более мощными эффектами. Например, эффекты Reader и Writer могут быть использованы в State, потому что State может читать и писать и таким образом изменять хранимое значение:
lift put :: Accumulated _ t => t _
lift get :: Configured _ t => t _
(lift . adapt $ put) :: Stateful _ t => t _
(lift . adapt $ get) :: Stateful _ t => t _
Как такое возможно? В предыдущей статье мы поделили State на два эффекта:
State s = (->) s :. (,) s
В случае с Reader, мы просто поднимаем функтор стрелки до уровня State, а в случае с Writer — функтор кортежа:
Reader s = (->) s
Writer s = (,) s
Мы можем адаптировать Failable к Optional, но мы потеряем информацию об ошибке:
(lift $ Just _) :: Optional t => t _
(lift $ failure _) :: Failable _ t => t _
(lift . adapt $ failure _) :: Optional t => t _
Чтобы проинтерпретировать какой-нибудь эффект в трансформере, достаточно одного метода run:
let xy = x *> y :: Reader _ :> State _ :> Either _ :> Maybe := _
let xy' = run xy _ :: State _ :> Either _ :> Maybe := _
let xy'' = run xy' _ :: Either _ :> Maybe := _
let xy''' = run xy'' :: Maybe (Either _) _
Вот так, уже сейчас, без свободных/свободнейших монад (а иногда даже и без монад), используя joint [3] вы можете типизировать свои выражения согласно эффектам, которые они производят. Все, что вам нужно это композиция функторов.
Еще есть презентация [4] с доклада по этой теме на локальном митапе [5] в Ростове-на-Дону, которую можно полистать в браузере.
Лучшие примеры — которые максимально приближены к реальности. Я люблю музыку, поэтому вы можете посмотреть, как используется эта система эффектов в программе, скачивающей альбомы с Bandcamp [6].
Автор: Мурат Касимов
Источник [7]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/haskell/344535
Ссылки в тексте:
[1] первую статью: https://habr.com/ru/post/467683/
[2] Стивену Дилю: http://www.stephendiehl.com/posts/decade.html
[3] joint: https://github.com/iokasimov/joint
[4] презентация: https://plexor.tech/storage/2020_jan_17/Kasimov/
[5] митапе : https://vk.com/lx61.meetup2
[6] программе, скачивающей альбомы с Bandcamp: https://github.com/iokasimov/bandcamp.albums
[7] Источник: https://habr.com/ru/post/485518/?utm_source=habrahabr&utm_medium=rss&utm_campaign=485518
Нажмите здесь для печати.