2008-11-20 6 views
21

Donc, j'ai une paire de typeclasses que je vais utiliser beaucoup ensemble, et je veux éviter de spécifier les deux à chaque fois. En fait, au lieu de mettreHaskell Typeclass raccourci

:: (Ord a, Fractional a, Ord b, Fractional b, ... Ord z, Fractional z) => 

au début de toutes mes spécifications de type, je préfère mettre

:: (OrdFractional a, OrdFractional b, ... OrdFractional z) 

Alors, ma première idée sur la façon de le faire était de déclarer juste une nouvelle classe de types

module Example where 

class (Fractional a, Ord a) => OrdFractional a 

example :: (OrdFractional a, OrdFractional b) => (a,b) -> (a,b) -> (a,b) -> Bool 
example (x1,y1) (x2,y2) (x3,y3) = (x1/x2 < x2/x3) && (y1/y2 < y2/y3) 

Mais cela ne fonctionne pas aussi automagiquement que je voulais qu'elle:

% ghci 
Prelude> :l Example.hs 
Ok, modules loaded: Example. 
Prelude Example> example (1::Float,3::Float) (2,2) (3,1) 

<interactive>:1:0: 
    No instance for (OrdFractional Float) 
     arising from a use of `example' at <interactive>:1:0-39 
    Possible fix: 
     add an instance declaration for (OrdFractional Float) 
    In the expression: example (1 :: Float, 3 :: Float) (2, 2) (3, 1) 
    In the definition of `it': 
     it = example (1 :: Float, 3 :: Float) (2, 2) (3, 1) 

Création manuelle des instances semble comme un glisser si, à côté, je pensais que je pourrais essayer de créer automatiquement des instances:

module Example where 

class OrdFractional a 
instance (Fractional a, Ord a) => OrdFractional a 

example :: (OrdFractional a, OrdFractional b) => (a,b) -> (a,b) -> (a,b) -> Bool 
example (x1,y1) (x2,y2) (x3,y3) = (x1/x2 < x2/x3) && (y1/y2 < y2/y3) 

Mais le compilateur ne pas comme ça:

ghc -c Example.hs 

Example.hs:4:0: 
    Illegal instance declaration for `OrdFractional a' 
     (All instance types must be of the form (T a1 ... an) 
     where a1 ... an are type *variables*, 
     and each type variable appears at most once in the instance head. 
     Use -XFlexibleInstances if you want to disable this.) 
    In the instance declaration for `OrdFractional a' 

Ainsi est il y a un moyen que je peux le faire?

Répondre

32

Avec l'extension ConstraintKinds introduit dans GHC 7.4, les contraintes sont maintenant types de type Constraint, vous pouvez utiliser des synonymes de type ordinaire pour obtenir ce que vous voulez:

{-# LANGUAGE ConstraintKinds #-} 

type OrdFractional a = (Ord a, Fractional a) 
10

Lorsque le compilateur dit « Use -XFlexibleInstances », vous devriez essayer d'ajouter

{-# LANGUAGE FlexibleInstances #-} 

au sommet de la source (et allez lire la documentation pour apprendre ce qu'il fait, bien sûr!).

Dans ce cas précis, cela rendra votre travail de code:

{-# LANGUAGE FlexibleInstances, UndecidableInstances #-} 

instances flexibles sont nécessaires afin de permettre le contexte => sur la tête de l'instance et les instances indécidables sont nécessaires parce que le compilateur, lors de la manipulation un contexte OrdFractional a, peut terminer l'ajout Fractional a et Ord a au contexte - ce qui ne contribue pas directement à la détermination finale a, et dans des circonstances convenablement horribles, typochecking peut diverger; le compilateur n'aime vraiment pas ça. (Vous auriez probablement pas comme si le compilateur a continué pour toujours ou a manqué de mémoire, non plus.)

3

n °

Votre solution d'un superclasse impliquant les autres classes est le plus proche de ce que vous voulez que est possible à Haskell. Même si cela nécessite des instances manuelles de cette nouvelle classe, il est parfois utilisé, par exemple dans la bibliothèque rewriting. Comme CesarB a mentionné que les alias de classe font ce que vous voulez (et plus), mais ils sont juste une proposition qui existe depuis des années et qui n'a jamais été implémentée, probablement parce qu'il y a de nombreux problèmes. Au lieu de cela, diverses autres propositions ont surgi, mais aucune de celles-ci n'a été mise en œuvre non plus. (Pour une liste de ces propositions, voir ce Haskellwiki page.) Un des projets à Hac5 était de modifier le GHC pour inclure un petit sous-ensemble d'alias de classe appelé les synonymes de contexte (qui font exactement ce que vous demandez ici et rien de plus), mais malheureusement il n'a jamais été fini.

+3

La nouvelle extension ConstraintKinds devrait permettre des synonymes de contexte. –