2017-05-03 6 views
4

Je joue avec une bibliothèque d'enregistrements extensible, et je veux écrire une fonction field qui peut fonctionner comme Lens ou Traversal selon que la clé Symbol soit dans la liste des clés. La famille de type est donné:Les familles de types ne peuvent pas renvoyer un type RankN - solutions de contournement ou alternatives?

type family LensOrTraversal key keys s t a b where 
    LensOrTraversal key '[] s t a b = 
     Traversal s t a b 
    LensOrTraversal key (key =: val ': xs) s t a b = 
     Lens s t a b 
    LensOrTraversal key (foo =: bar ': xs) s t a b = 
     LensOrTraversal key xs s t a b 

Ce code me donne une erreur:

/home/matt/Projects/hash-rekt/src/Data/HashRecord/Internal.hs:433:5: 
error: 
    • Illegal polymorphic type: Traversal s t a b 
    • In the equations for closed type family ‘LensOrTraversal’ 
     In the type family declaration for ‘LensOrTraversal’ 

Idéalement, je voudrais être en mesure de réutiliser le nom field pour les lentilles et traversals, comme il le ferait vous permettent d'écrire

>>> let testMap = empty & field @"foo" .~ 'a' 
>>> :t testMap 
HashRecord '["foo" =: Char] 
>>> testMap ^. field @"foo" 
'a' 
>>> testMap ^. field @"bar" 
Type error 
>>> testMap ^? field @"bar" 
Nothing 

qui suit lens communs idiomes. Je peux fournir une fonction fieldTraversal qui fait ce que je veux, mais je préférerais surcharger le nom field si possible. Comment travailleriez-vous autour de cette limitation des familles de caractères?

+0

double possible de [illégaux de type polymorphes ou qualifiés en utilisant RankNTypes et TypeFamilies] (http://stackoverflow.com/questions/13846284/illegal-polymorphic-or-qualified-type-using-rankntypes-and-typefamilies) –

+0

@ AntalSpector-Zabusky peut-être lié, mais ce n'est certainement pas un doublon. – leftaroundabout

Répondre

4

Une lentille est déjà un traversal, seul son quantificateurs Rank2 ne fait pas usage de la contrainte complète (il faut simplement Functor, non Applicative).

type Lens s t a b  = ∀ f . Functor f  => (a -> f b) -> s -> f t 
type Traversal s t a b = ∀ f . Applicative f => (a -> f b) -> s -> f t 

Il est au niveau de cette contrainte que vous devez présenter votre famille de type:

import GHC.Exts (Constraint) 
type family FieldOpticConstraint key keys :: (* -> *) -> Constraint where 
    FieldOpticConstraint key '[] = Applicative 
    FieldOpticConstraint key (key =: val ': xs) = Functor 
    FieldOpticConstraint key (_ ': xs) = FieldOpticConstraint key xs 

Alors field ne devrait pas donner un LensOrTraversal, mais toujours une coutume Rank2 signature avec la contrainte déterminé par la famille de caractères.

+0

Bien! Si seulement nous avions des contraintes d'implication, nous pourrions exprimer des choses astucieuses comme "doit au moins être un' Traversal' ". J'ai demandé de telles idées il y a quelque temps. Avez-vous une manière délicate d'exprimer le concept sans manipulation explicite du dictionnaire? – dfeuer

+0

Ouf, aucune idée. Je soupçonne qu'il ouvrirait un peu de boîte de Pandore si nous essayions d'obtenir de telles contraintes à partir de types polymorphes arbitraires. Est-ce même décidable? – leftaroundabout

+0

Bien! Cela a résolu mon problème. Maintenant, j'ai juste besoin de comprendre comment traiter cette information au niveau de la valeur ... – ephrion