2012-10-03 12 views
7

J'ai un certain type artificiel:contrainte polymorphes

{-# LANGUAGE DeriveFunctor #-} 

data T a = T a deriving (Functor) 

... et ce type est l'instance d'une classe artificielle:

class C t where 
    toInt :: t -> Int 

instance C (T a) where 
    toInt _ = 0 

Comment puis-je exprimer une contrainte de fonction qui est T a une instance de classe pour tous a?

Par exemple, considérons la fonction suivante:

f t = toInt $ fmap Left t 

Intuitivement, je me attends à la fonction ci-dessus fonctionne depuis toInt travaux sur T a pour tous a, mais je ne peux exprimer que dans le type. Cela ne fonctionne pas:

f :: (Functor t, C (t a)) => t a -> Int 

... parce que quand nous appliquons fmap le type est devenu Either a b. Je ne peux pas résoudre ce problème en utilisant:

f :: (Functor t, C (t (Either a b))) => t a -> Int 

... parce que b ne représente pas une variable universellement quantifiée. Je ne peux pas dire:

f :: (Functor t, C (t x)) => t a -> Int 

... ou utiliser forall x pour suggérer que la contrainte est valable pour tous les x. Donc, ma question est de savoir s'il existe un moyen de dire qu'une contrainte est polymorphe sur certaines de ses variables de type.

+0

Je suppose que quelque chose comme 'classe C t où toInt :: ta -> Int' ne fonctionne pas, et vous avez besoin' C' pour être de type '* -> Contrainte'? Le polymorphisme gentil aiderait-il ici? –

+0

@ C.A.McCann Le constructeur de type concret que j'ai à l'esprit est 'Proxy' de' pipes' et la classe concrète est 'Monad'. Je classe les fonctions utilitaires de type proxy pour les types de type proxy, c'est pourquoi la contrainte est là. Suite à votre suggestion, je définirais alors une classe 'MonadP' spécialisée dans la forme du constructeur de type' Proxy' et l'utiliserais comme une contrainte à la place. L'inconvénient est que si les utilisateurs voulaient écrire des fonctions d'utilitaires proxy polymorphes dans le type proxy, ils devraient redéfinir la notation pour utiliser 'MonadP' à la place. –

+2

Vous ne pouvez pas le faire directement, mais il est possible de simuler, comme dans la réponse de Roman. Voici le ticket GHC pertinent: http://hackage.haskell.org/trac/ghc/ticket/2893 – glaebhoerl

Répondre

6

Utilisation du package constraints:

{-# LANGUAGE FlexibleContexts, ConstraintKinds, DeriveFunctor, TypeOperators #-} 

import Data.Constraint 
import Data.Constraint.Forall 

data T a = T a deriving (Functor) 

class C t where 
    toInt :: t -> Int 

instance C (T a) where 
    toInt _ = 0 

f :: ForallF C T => T a -> Int 
f t = (toInt $ fmap Left t) \\ (instF :: ForallF C T :- C (T (Either a b))) 
+3

Il vaut aussi la peine de regarder la source de ce 'ForallF', ne serait-ce que pour admirer comment simple/laid/terrifiant l'idée derrière c'est :) – copumpkin

+0

@copumpkin: J'ai vraiment une question à ce sujet - http://stackoverflow.com/questions/12728159/how-does-the-constraints-package-work –