Варианты использования для дополнений в Haskell - PullRequest
11 голосов
/ 12 июня 2019

Я читал о дополнениях в течение последних нескольких дней. Хотя я начинаю понимать их важность с теоретической точки зрения, мне интересно, как и почему люди используют их в Haskell. Data.Functor.Adjunction обеспечивает реализацию и среди его примеров: бесплатный функтор / Forgotful функтор и curry / uncurry . Опять же, они очень интересны с теоретической точки зрения, но я не понимаю, как бы я использовал их для более практических задач программирования.

Существуют ли примеры проблем программирования, которые люди решали с помощью Data.Functor.Adjunction, и почему вы бы предпочли эту реализацию другим?

1 Ответ

3 голосов
/ 21 июня 2019

Предварительное примечание: этот ответ немного умозрительный.Во многом как вопрос, он был построен из изучения Data.Functor.Adjunction.

Я могу думать о трех причинах, почему не так много вариантов использования для класса Adjunction в дикой природе.

Во-первых, все присоединения Hask / Hask в конечном итоге представляют собой некоторую вариацию присоединения каррирования, поэтому спектр потенциальных экземпляров не так уж велик для начала.Многие дополнения, которые могут вас заинтересовать, не являются Hask / Hask.

Во-вторых, в то время как экземпляр Adjunction дает вам откровенно потрясающее количество других экземпляров бесплатно, во многих случаях эти экземпляры уже где-то существуютостальное.Чтобы выбрать пример ур, мы могли бы очень легко реализовать StateT в терминах Control.Monad.Trans.Adjoint:

newtype StateT s m a = StateT { runStateT :: s -> m (s, a) }
  deriving (Functor, Applicative, Monad) via AdjointT ((,) s) ((->) s) m
  deriving MonadTrans via AdjointT ((,) s) ((->) s)
  -- There is also a straightforward, fairly general way to implement MonadState.

Однако на самом деле никому не нужно это делать, потому что естьсовершенно хорошие StateT в трансформаторы .Тем не менее, если у вас есть Adjunction собственный экземпляр, вам может повезти.Одна небольшая вещь, о которой я подумала, может иметь смысл (даже если я на самом деле ее не видела) - это следующие функторы:

data Dilemma a = Dilemma { fstDil :: a, sndDil a }

data ChoiceF a = Fst a | Snd a

Мы могли бы написать экземпляр Adjunction ChoiceF Dilemma, который отражает, какDilemma (ChoiceF a) является материализованной версией State Bool a.Dilemma (ChoiceF a) можно рассматривать как шаг в дереве решений: выбор одной из сторон Dilemma через конструкторов ChoiceF говорит о том, какой выбор следует сделать дальше.Экземпляр Adjunction тогда даст нам монадный преобразователь на Dilemma (ChoiceF a) бесплатно.

(Другой возможностью может быть использование Free f / Cofree u присоединения . Cofree Dilemma aэто бесконечное дерево результатов, в то время как Free ChoiceF a - это путь, ведущий к результату. Я рискую, что из этого есть некоторый путь.)

В-третьих, в то время как есть много полезных функций для правильных примыканий вData.Functor.Adjunction, большинство функций, которые они предоставляют, также доступны через Representable и / или Distributive, поэтому большинство мест, где они могут быть использованы, вместо этого останавливаются на суперклассах.

Data.Functor.Adjunction,конечно, также предлагает полезные функции для left присоединений.С одной стороны, левые примыкания (которые изоморфны парам, то есть контейнерам, содержащим один элемент), вероятно, менее универсальны, чем правые примыкания (которые изоморфны функциям, то есть функторам с одной формой);с другой стороны, кажется, что нет никакого канонического класса для левых примыканий (по крайней мере, пока), что может привести к возможности фактически использовать Data.Functor.Adjunction функции.Между прочим, пример линкора Криса Пеннера , который вы предложили, возможно, соответствует счету, поскольку он полагается на левый сопряженный и как он может использоваться для кодирования представления правого сопряженного:

zapWithAdjunction :: Adjunction f u => (a -> b -> c) -> u a -> f b -> c
zapWithAdjunction @CoordF @Board :: (a -> b -> c) -> Board a -> CoordF b -> c

checkHit :: Vessel -> Weapon -> Bool

shoot :: Board Vessel -> CoordF Weapon -> Bool

CoordF, левый сопряженный, содержит координаты для доски и полезной нагрузки.zapWithAdjunction позволяет (буквально в данном случае) нацеливать позицию при использовании полезной нагрузки.

...