2016-07-12 1 views
2

Je trompe avec des lentilles et des prismes et je suis un peu dans les mauvaises herbes. Je veux écrire ce qui suit dans le modèle Haskell mais est-il ne compile pas:Comment puis-je faire (Fold s a) à partir de Control.Lens un monoid?

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

Tout ce que je sais au sujet des lentilles et des prismes (ainsi que les commentaires dans la documentation Getter) suggère que _Spade <> _Club devrait être un Fold Suit() valide . Mais ghc ne peut pas compiler le code ci-dessus; il se plaint de plusieurs cas ambigus. ghci me donne la signature de type suivant pour _Spade <> _Club:

_Spade <> _Club 
    :: (Applicative f, Monoid (p Suit (f Suit)), Choice p) => 
    p() (f()) -> p Suit (f Suit) 

Et en remplaçant p avec (->) je peux détendre ce type à

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

La seule différence entre cela et Fold Suit() est que ce dernier a (Applicative f, Contravariant f) => ... place de la restriction Monoid (f Suit). Quand je lis sur Contravariant je vois des assertions que (Applicative f, Contravariant f) impliquent ensemble que f est réellement Const r pour certains r monoid. Cela semble suggérer que le type ci-dessus est en fait un sous-ensemble de Fold Suit(). Mais lorsque je tente d'ajouter blackSuits :: Fold Suit() à mon code, je reçois

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

Je reçois des erreurs similaires si j'essaie juste de définir monoids sur les plis au lieu de commencer par des prismes. Rien que j'ai fait pour essayer de faire Fold s a un Monoid passe le compilateur. Y a-t-il un moyen de faire fonctionner cela?

Répondre

4

Si vous regardez la signature Fold:

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

il dit qu'il doit travailler pour tousf qui sont Contravariant et Applicative. Lorsque vous combinez deux fois ensemble en utilisant <>, l'instance Semigroup pour les fonctions instance Semigroup b => Semigroup (a -> b) requiert que f s soit Semigroup. Cela signifie qu'il ne sera plus saisir comme Fold. Cependant, la plupart (toutes?) Fonctions qui prennent un pli en lens l'accepteront encore parce qu'ils prennent un Getting r s a = (a -> Const r a) -> s -> Const r s, ce que ce type va vérifier.

Il y a plusieurs choses que vous pouvez faire. Vous pouvez utiliser le Fold newtype qui a son propre Monoid exemple:

> :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() 

Ou vous pouvez « cloner » le pli:

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

ou, si vous ne avez pas besoin d'être vous Fold peut utiliser Getting synonyme qui travaillerait la même chose dans la plupart des cas:

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

Ah, c'est ce que « réifiée » plis sont pour! Merci! –