2017-03-17 4 views
0

J'ai un type de données qui n'a de sens que si ses arguments peuvent être ordonnés, mais il me semble avoir besoin d'approfondir certains trucs complexes et potentiellement hacks pour le faire fonctionner (GADTs, principalement). Ce que je fais (les types de données contraints) est-il considéré comme une mauvaise pratique du haschell, et y a-t-il un moyen de contourner ce problème?Contexte dans les instances de données

Pour les intéressés, voici le code correspondant:

{-# LANGUAGE GADTs #-} 
{-# LANGUAGE InstanceSigs #-} 
import Data.List (sort) 

data OrdTriple a where 
    OrdTriple :: (Ord a) => a -> a -> a -> OrdTriple a 

instance Functor OrdTriple where 
    fmap :: (Ord a, Ord b) => (a -> b) -> OrdTriple a -> OrdTriple b 
    fmap f (OrdTriple n d x) = OrdTriple n' d' x' 
     where 
      [n', d', x'] = sort [f n, f d, f x] 

Au début, je pensais que je venais de mettre un contexte dans l'instance Functor (c'est le seul exemple que je me bats avec), mais il semble que je ne peut pas (aucune mention du type contenu), et même si je pouvais encore avoir besoin de la contrainte sur fmap que son type de retour puisse être commandé.

Comme il est, je reçois l'erreur suivante compilation, qu'il semble est parce que je suis trop contraignant l'instance Functor:

No instance for (Ord a) 
Possible fix: 
    add (Ord a) to the context of 
    the type signature for 
     fmap :: (a -> b) -> OrdTriple a -> OrdTriple b 
When checking that: 
    forall a b. 
    (Ord a, Ord b) => 
    (a -> b) -> OrdTriple a -> OrdTriple b 
    is more polymorphic than: 
    forall a b. (a -> b) -> OrdTriple a -> OrdTriple b 
When checking that instance signature for ‘fmap’ 
    is more general than its signature in the class 
    Instance sig: forall a b. 
       (Ord a, Ord b) => 
       (a -> b) -> OrdTriple a -> OrdTriple b 
    Class sig: forall a b. (a -> b) -> OrdTriple a -> OrdTriple b 
In the instance declaration for ‘Functor OrdTriple’ 
+3

Functor doit être applicable à tous les types, non seulement ceux qui sont contraints par 'Ord'. Vous ne pouvez pas le contraindre davantage même dans votre cas. C'est pourquoi les structures comme les arbres équilibrés ne peuvent pas non plus être des foncteurs. – bheklilr

+3

Vous ne pouvez pas en faire un foncteur. La manière habituelle de contourner cela est alors de simplement implémenter une fonction "map" séparée, comme cela est fait pour 'Data.Set' et' Data.Map'. – Cubic

Répondre

1

Vous ne pouvez pas le faire en utilisant la classe standard Functor, puisque son fmap doit fonctionner sur tous les types de données, sans contraintes.

Vous pourriez travailler avec une classe différente. Une option consiste à utiliser une classe "foncteur à grain fin" qui vous permet d'utiliser une instance distincte pour chaque paire de types a b. (Probablement ce qui a déjà un nom standard, mais je ne me souviens pas)

class FgFunctor f a b where 
    fgmap :: (a->b) -> f a -> f b 

-- regular functors are also fine-grained ones, e.g. 
instance FgFunctor [] a b where 
    fgmap = fmap 

instance (Ord a, Ord b) => FgFunctor OrdTriple a b where 
    fgmap f (OrdTriple n d x) = OrdTriple n' d' x' 
     where [n', d', x'] = sort [f n, f d, f x] 

Alternativement, on peut paramétrer la classe Functor avec une contrainte:

{-# LANGUAGE GADTs, KindSignatures, MultiParamTypeClasses, 
    ConstraintKinds, TypeFamilies, FlexibleInstances #-} 
{-# OPTIONS -Wall #-} 
module CFunctor where 

import Data.List (sort) 
import Data.Kind (Constraint) 

data OrdTriple a where 
    OrdTriple :: (Ord a) => a -> a -> a -> OrdTriple a 

class CFunctor (f :: * -> *) where 
    type C f a :: Constraint 
    cmap :: (C f a, C f b) => (a -> b) -> f a -> f b 

-- regular functors are also constrained ones, e.g. 
instance CFunctor [] where 
    type C [] a = a ~ a 
    cmap = fmap 

instance CFunctor OrdTriple where 
    type C OrdTriple a = Ord a 
    cmap f (OrdTriple n d x) = OrdTriple n' d' x' 
     where [n', d', x'] = sort [f n, f d, f x] 
+0

Votre IxFunctor est-il lié à [this] (http://stackoverflow.com/a/27771772/6112457) ou (légèrement différent) [this] (https://hackage.haskell.org/package/indexed-0.1. 3/docs/Data-Functor-Indexed.html)? Je ne peux pas dire si elles représentent des concepts similaires d'une manière que je ne vois pas, ou si elles sont complètement différentes. –

+1

@ZoeyHewll Il semble que j'ai bousillé la terminologie: celle ci-dessus n'est pas un foncteur indexé 'f i j a', mais quelque chose d'autre. Je ne me souviens pas de son nom propre, mais je serais surpris si cela ne se trouve pas quelque part. Je vais modifier la terminologie pour ne pas créer de confusion. Merci. – chi

+0

Pour CFunctor, je reçois 'Non dans la portée: constructeur de type ou de la classe 'Constraint'' –