我在镜头和棱镜上鬼混,我已经进入了杂草。我想在模板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
对于某些幺半群Const r
实际上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
通过编译器。有没有办法让这项工作?
答案 0 :(得分:4)
如果你看一下Fold
签名:
type Fold s a = forall f. (Contravariant f, Applicative f)=> (a -> f a) -> s -> f s
它说它必须适用于f
和Contravariant
的所有 Applicative
。当您使用<>
将两个折叠组合在一起时,函数Semigroup
的{{1}}实例要求instance Semigroup b => Semigroup (a -> b)
为f s
。这意味着它将不再将检查类型设为Semigroup
。但是,大多数(所有?)在Fold
中进行折叠的函数仍然会接受这个函数,因为它们会使用lens
,这会键入check。
你可以做几件事。您可以使用Fold
newtype,它具有自己的Getting r s a = (a -> Const r a) -> s -> Const r s
实例:
Monoid
或者你可以&#34;克隆&#34;折叠:
> :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 ()
或者,如果您不需要它是> let cloneFold = foldring . foldrOf
> let blackSuitsClone = cloneFold $ _Spade <> _Club :: Fold Suit ()
,您可以使用Fold
同义词,这在大多数情况下都会起作用:
Getting