Предварительное примечание: этот ответ немного умозрительный.Во многом как вопрос, он был построен из изучения 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
позволяет (буквально в данном случае) нацеливать позицию при использовании полезной нагрузки.