Recherche…


Type Synonyme Familles

Les familles de types synonyme ne sont que des fonctions de type: elles associent les types de paramètres aux types de résultats. Ceux-ci viennent dans trois variétés différentes.

Familles de type synonyme fermé

Celles-ci fonctionnent beaucoup comme les fonctions Haskell ordinaires au niveau de la valeur: vous spécifiez des clauses, en mappant certains types à d'autres:

{-# LANGUAGE TypeFamilies #-}
type family Vanquisher a where
    Vanquisher Rock = Paper
    Vanquisher Paper = Scissors
    Vanquisher Scissors = Rock

data Rock=Rock; data Paper=Paper; data Scissors=Scissors

Familles ouvertes de type synonyme

Celles-ci fonctionnent plus comme des instances de type typeclass: tout le monde peut ajouter d'autres clauses dans d'autres modules.

type family DoubledSize w

type instance DoubledSize Word16 = Word32
type instance DoubledSize Word32 = Word64
-- Other instances might appear in other modules, but two instances cannot overlap
-- in a way that would produce different results.

Synonymes de type associé à la classe

Une famille de type ouvert peut également être combinée avec une classe réelle. Cela se fait généralement lorsque, comme avec les familles de données associées , une méthode de classe nécessite des objets auxiliaires supplémentaires, et que ces objets peuvent être différents pour différentes instances, mais peuvent également être partagés. Un bon exemple est la classe VectorSpace :

class VectorSpace v where
  type Scalar v :: *
  (*^) :: Scalar v -> v -> v

instance VectorSpace Double where
  type Scalar Double = Double
  μ *^ n = μ * n

instance VectorSpace (Double,Double) where
  type Scalar (Double,Double) = Double
  μ *^ (n,m) = (μ*n, μ*m)
  
instance VectorSpace (Complex Double) where
  type Scalar (Complex Double) = Complex Double
  μ *^ n = μ*n

Notez que dans les deux premières instances, l'implémentation de Scalar est la même. Cela ne serait pas possible avec une famille de données associée: les familles de données sont injectives , les familles de type synonyme ne le sont pas.

Bien que la non-injectivité ouvre certaines possibilités comme ci-dessus, cela rend aussi l'inférence de type plus difficile. Par exemple, ce qui suit ne sera pas typique:

class Foo a where
  type Bar a :: *
  bar :: a -> Bar a
instance Foo Int where
  type Bar Int = String
  bar = show
instance Foo Double where
  type Bar Double = Bool
  bar = (>0)

main = putStrLn (bar 1)

Dans ce cas, le compilateur ne peut pas savoir quelle instance utiliser, car l'argument de bar est lui-même un littéral polymorphe Num . Et la fonction de type Bar ne peut pas être résolue en «sens inverse», précisément parce qu'elle n'est pas injective et n'est donc pas inversible (il peut y avoir plus d'un type avec Bar a = String ).


Avec seulement ces deux instances, il est en réalité injectif, mais le compilateur ne peut pas savoir que quelqu'un ajoutera plus d'instances ultérieurement et rompra ainsi le comportement.

Familles de types de données

Les familles de données peuvent être utilisées pour créer des types de données ayant des implémentations différentes basées sur leurs arguments de type.

Familles de données autonomes

{-# LANGUAGE TypeFamilies #-}
data family List a
data instance List Char = Nil | Cons Char (List Char)
data instance List () = UnitList Int

Dans la déclaration ci-dessus, Nil :: List Char et UnitList :: Int -> List ()

Familles de données associées

Les familles de données peuvent également être associées à des classes de caractères. Ceci est souvent utile pour les types avec des «objets auxiliaires», qui sont requis pour les méthodes génériques de typeclass mais qui doivent contenir des informations différentes selon l'instance concrète. Par exemple, l'indexation des emplacements dans une liste ne nécessite qu'un seul numéro, tandis que dans un arbre, vous avez besoin d'un numéro pour indiquer le chemin d'accès à chaque noeud:

class Container f where
  data Location f
  get :: Location f -> f a -> Maybe a

instance Container [] where
  data Location [] = ListLoc Int
  get (ListLoc i) xs
    | i < length xs  = Just $ xs!!i
    | otherwise      = Nothing

instance Container Tree where
  data Location Tree = ThisNode | NodePath Int (Location Tree)
  get ThisNode (Node x _) = Just x
  get (NodePath i path) (Node _ sfo) = get path =<< get i sfo

L'injectivité

Type Les familles ne sont pas nécessairement injectables. Par conséquent, nous ne pouvons pas déduire le paramètre d'une application. Par exemple, dans servant , étant donné un type Server a nous ne pouvons pas déduire le type a . Pour résoudre ce problème, nous pouvons utiliser Proxy . Par exemple, en servant , le serve la fonction est de type ... Proxy a -> Server a -> ... . Nous pouvons en déduire a de Proxy a car Proxy est défini par des data qui sont injectives.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow