我如何从 Control.Lens 制作 (Fold s a) 一个幺半群?

How can I make (Fold s a) from Control.Lens a monoid?

我在玩弄透镜和棱镜,我已经有点杂草了。我想在模板 Haskell 中编写以下内容,但它无法编译:

data Suit = Spade | Heart | Diamond | Club
makePrisms ''Suit
blackSuits = _Spade <> _Club

我对透镜和棱镜的了解(以及 Getter 文档中的评论)表明 _Spade <> _Club 应该是有效的 Fold Suit ()。但是ghc无法编译上面的代码;它抱怨几个模棱两可的例子。 ghci 为 _Spade <> _Club:

提供了以下类型签名
_Spade <> _Club
  :: (Applicative f, Monoid (p Suit (f Suit)), Choice p) =>
     p () (f ()) -> p Suit (f Suit)

并且通过将 p 替换为 (->) 我可以放宽该类型为

(Applicative f, Monoid (f Suit)) => (() -> f ()) -> Suit -> f Suit

它与 Fold Suit () 的唯一区别是后者有 (Applicative f, Contravariant f) => ... 而不是 Monoid (f Suit) 限制。当我阅读 Contravariant 时,我看到断言 (Applicative f, Contravariant f) 一起暗示 f 对于某些幺半群 r 实际上是 Const r。这似乎表明上面的类型实际上是 Fold Suit () 的一个子集。但是当我尝试将 blackSuits :: Fold Suit () 添加到我的代码时,我得到

Could not deduce (Monoid (f Suit)) arising from a use of ‘<>’
from the context (Contravariant f, Applicative f) ...

如果我只是尝试在折叠上定义幺半群而不是从棱镜开始,我会遇到类似的错误。我没有尝试让 Fold s a 成为 Monoid 通过编译器。有什么办法可以做到这一点吗?

如果您查看 Fold 签名:

type Fold s a = forall f. (Contravariant f, Applicative f)=> (a -> f a) -> s -> f s

它说它必须适用于 所有 f,即 ContravariantApplicative。当您使用 <> 将两个折叠组合在一起时,函数 instance Semigroup b => Semigroup (a -> b)Semigroup 实例要求 f sSemigroup。这意味着它将不再将检查类型作为 Fold。但是,大多数(全部?)在 lens 中折叠的函数仍然会接受它,因为它们会在 Getting r s a = (a -> Const r a) -> s -> Const r s 中进行类型检查。

您可以做几件事。您可以使用 Fold 新类型,它有自己的 Monoid 实例:

> :set -XTemplateHaskell
> import Control.Lens
> import Data.Semigroup
> data Suit = Spade | Heart | Diamond | Club; makePrisms ''Suit
> let blackSuitsFold = runFold $ Fold _Spade <> Fold _Club :: Fold Suit ()

或者你可以"clone"弃牌:

> let cloneFold = foldring . foldrOf
> let blackSuitsClone = cloneFold $ _Spade <> _Club :: Fold Suit ()

或者,如果您不需要它成为 Fold,您可以使用 Getting 同义词,这在大多数情况下都是一样的:

> let blackSuitsGetter = _Spade `mappend` _Club :: Monoid r => Getting r Suit ()
> has blackSuitsGetter Spade
True