42 votes

Y a-t-il un type 'Any' dans haskell?

Dis, je veux définir un attribut record comme ceci:

data Attribute = Attribute {name :: String, value :: Any}

Ce n'est pas un code haskell valide, bien sûr. Mais existe-t-il un type 'Any' qui dit fondamentalement que tout type fera l'affaire? Ou est-ce d'utiliser la variable de type le seul moyen?

data Attribute a = Attribute {name :: String, value :: a}

68voto

bdonlan Points 90068

Généralement parlant, Any types ne sont pas très utiles. Pensez-y: Si vous faites une liste polymorphe qui peut contenir n'importe quoi, que pouvez-vous faire avec les types dans la liste? La réponse, bien sûr, n'est rien d' - vous n'avez aucune garantie qu'il n'y est aucune opération commune à ces éléments.

Ce que l'on va en général, c'est soit:

  1. Utiliser les GADTs à faire une liste qui peut contenir des éléments spécifiques d'un typeclass, comme dans:

    data FooWrap where
        FooWrap :: Foo a => a -> FooWrap
    type FooList = [FooWrap]
    

    Avec cette approche, vous ne connaissez pas le type de béton des éléments, mais vous savez qu'ils peuvent être manipulés à l'aide d'éléments de l' Foo typeclass.

  2. Créer un type de basculer entre les différents types de béton sont contenues dans la liste:

    data FooElem = ElemFoo Foo | ElemBar Bar
    type FooList = [FooElem]
    

    Cela peut être combinée avec l'approche 1 pour créer une liste qui peut contenir des éléments qui sont de l'un d'un ensemble fixe de typeclasses.

  3. Dans certains cas, il peut être utile de créer une liste de fonctions de manipulation:

    type FooList = [Int -> IO ()]
    

    Ceci est utile pour des choses comme les systèmes de notification d'événement. Au moment de l'ajout d'un élément à la liste, vous lier dans une fonction qui effectue quelle que soit la manipulation que vous aurez plus tard voulez faire.

  4. Utiliser Data.Dynamic (non recommandé!) comme de la triche. Toutefois, cela n'offre aucune garantie d'un élément spécifique peut être manipulé à tous, et donc les approches ci-dessus devraient être privilégiées.

21voto

Mikhail Glushenkov Points 10348

L'ajout de bdonlan réponse: au Lieu de GADTs, vous pouvez également utiliser existentielle types:

{-# LANGUAGE ExistentialQuantification #-}

class Foo a where
  foo :: a -> a

data AnyFoo = forall a. Foo a => AnyFoo a

instance Foo AnyFoo where
  foo (AnyFoo a) = AnyFoo $ foo a

mapFoo :: [AnyFoo] -> [AnyFoo]
mapFoo = map foo

C'est en gros équivalent à bdonlan de GADT solution, mais ne pas imposer le choix de la structure de données sur vous - vous pouvez utiliser un Map au lieu d'une liste, par exemple:

import qualified Data.Map as M

mFoo :: M.Map String AnyFoo
mFoo = M.fromList [("a", AnyFoo SomeFoo), ("b", AnyFoo SomeBar)]

L' data AnyFoo = forall a. Foo a => AnyFoo a bit peut également être écrit en GADT notation comme:

data AnyFoo where
  AnyFoo :: Foo a => a -> AnyFoo

13voto

augustss Points 15750

Il y a le type Dynamic de Data.Dynamic qui peut contenir n'importe quoi (enfin, n'importe quoi Typeable ). Mais c'est rarement la bonne façon de le faire. Quel est le problème que vous essayez de résoudre?

12voto

Daniel Wagner Points 38831

Cela sonne comme une jolie question de base, je vais donc donner encore plus de réponses de base que n'importe qui d'autre. Voici ce qui est presque toujours la bonne solution:

data Attribute a = Attribute { name :: String, value :: a }

Puis, si vous voulez un attribut qui encapsule une Int, que l'attribut de type Attribute Int, ou un attribut qui encapsule une Bool auraient type Attribute Bool, etc. Vous pouvez créer ces attributs avec des valeurs de n'importe quel type; par exemple, on peut écrire

testAttr = Attribute { name = "this is only a test", value = Node 3 [] }

pour créer une valeur de type Attribute (Tree Int).

2voto

Edgar Klerks Points 600

Si vos données doivent éventuellement être d'un type spécifique, vous pouvez utiliser Convertible avec des GADT. En tant que consommateur, vous n'êtes intéressé que par le type de données que vous devez consommer.

 {-# LANGUAGE GADTs #-}
import Data.Convertible 

data Conv b where 
   Conv ::  a -> (a -> b) -> Conv b
   Chain :: Conv b -> (b -> c) -> Conv c

unconv :: (Conv b) -> b 
unconv (Conv a f) = f a
unconv (Chain c f) = f $ unconv c

conv :: Convertible a b => a -> Conv b 
conv a = (Conv a convert)

totype :: Convertible b c => Conv b -> Conv c
totype a = Chain a convert
 

Il n’est pas très difficile d’obtenir des occurrences functor, comonad et monad pour cela. Je peux les poster si cela vous intéresse.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X