在过去的几天里,我一直在阅读adjunctions。当我从理论的角度开始理解它们的重要性时,我想知道人们如何以及为什么人们在Haskell中使用它们。 Data.Functor.Adjunction
提供了一种实现,其实例包括free functor / forgetful函子和curry / uncurry。从理论上讲,这些再次很有趣,但是我看不到如何将它们用于更实际的编程问题。
是否存在人们使用Data.Functor.Adjunction
解决的编程问题的示例,以及为什么您更喜欢这种实现?
答案 0 :(得分:3)
初步提示:这个答案有点推测。与问题非常相似,它是通过研究Data.Functor.Adjunction
构建的。
我可以想到三个原因,说明Adjunction
类的使用案例很少。
首先,所有Hask / Hask附件最终都在循环附件上有所变化,因此潜在实例的范围并不那么大。人们可能会感兴趣的许多附件不是Hask / Hask。
第二,虽然Adjunction
实例免费为您提供了大量的其他实例,但在许多情况下,这些实例已经存在于其他地方。以我们的示例为例,我们可以很容易地根据Control.Monad.Trans.Adjoint
来实现StateT
:
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)
的monad转换器。
(另一种可能是利用the Free f
/Cofree u
adjunction。Cofree Dilemma a
是无限的结果树,而Free ChoiceF a
是通往结果的道路。我冒着走出去的麻烦
第三,虽然Data.Functor.Adjunction
中有许多用于正确伴随的有用功能,但它们提供的大多数功能也可以通过Representable
和/或Distributive
使用,因此大多数地方可能最终会坚持使用超类。
Data.Functor.Adjunction
还为 left 伴随项提供有用的功能。一方面,左连接(与线对同构,即容纳单个元素的容器)的通用性可能不如右连接(与功能,即具有单一形状的函子同构)。另一方面,似乎没有用于左伴随的规范类(至少现在还没有),因此可能会导致实际使用Data.Functor.Adjunction
函数的机会。顺便提一句,您建议Chris Penner's battleship example可以说是合适的,因为它确实依赖于左伴随项以及如何使用它来编码右伴随项的表示形式:
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
使得(在这种情况下,字面意义上)在使用有效负载时定位目标成为可能。