2011-06-25 9 views
38

Say, quiero definir un registro de atributos como esto:¿Hay algún tipo 'Any' en haskell?

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

Esto no es un código válido Haskell por supuesto. ¿Pero hay un tipo 'Cualquiera' que básicamente dice que cualquier tipo va a hacer? ¿O es usar la variable de tipo de la única manera?

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

+5

Mira en 'GHC.Prim.Any'. –

+0

Si desea que 'name' tenga realmente cualquier tipo, debe usar una variable de tipo. Pero: ¿estás seguro de que el 'nombre' de una persona debería tener algún tipo? Probablemente (por ejemplo) un 'Int'-value como' name' o un 'Bool'-value como nombre no es sonido? – phynfo

+0

@phynfo, la muestra que utilicé fue hipotética. Acabo de enmendarlo para que sea más general. – edwardw

Respuesta

65

En general, los tipos Any no son muy útiles. Considere: si usted hace una lista polimórfica que puede contener cualquier cosa, ¿qué puede hacer con los tipos en la lista? La respuesta, por supuesto, no es nada: no tienes garantía de que haya alguna operación común a estos elementos.

Lo que uno normalmente va a hacer es o bien:

  1. Uso GADTs para hacer una lista que puede contener elementos de una clase de tipos específicos, como en:

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

    Con este enfoque, Don No sé el tipo concreto de los elementos, pero sabe que pueden manipularse utilizando elementos de la clase de tipo Foo.

  2. Crear un tipo para conmutar entre los tipos de hormigón específicas contenidas en la lista:

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

    Esto se puede combinar con el enfoque 1 para crear una lista que puede contener elementos que son de uno de un conjunto fijo de clases de tipos

  3. En algunos casos, puede ser útil para construir una lista de funciones de manipulación:

    type FooList = [Int -> IO()] 
    

    Esto es útil para cosas como los sistemas de notificación de eventos. En el momento de agregar un elemento a la lista, lo enlaza en una función que realiza la manipulación que luego quiera hacer.

  4. Use Data.Dynamic (no se recomienda!) Como un truco. Sin embargo, esto no proporciona ninguna garantía de que un elemento específico pueda ser manipulado en absoluto, por lo que los enfoques anteriores deberían ser preferidos.

+2

+1 para sugerir los enfoques "adecuados". –

+0

Gracias por la respuesta completa, @bdonlan. Esto es lo que estoy buscando. – edwardw

+16

N.B. - Tenga en cuenta que 'Any' tipos son extremadamente útiles en * un idioma con subtipificación * - un ejemplo común es una clase' Object' en la parte superior de una jerarquía de herencia OOP. Haskell no tiene una noción real de subtipado, por lo que el concepto es en gran parte inútil aquí. –

12

No es el tipo Dynamic de Data.Dynamic que puede contener nada (bueno, nada Typeable). Pero rara vez es la forma correcta de hacerlo. ¿Cuál es el problema que estás tratando de resolver?

+0

Hola @augustss, una es que estoy buscando el equivalente del tipo any() de Erlang para poder traducir algún código. El otro es que me pregunto cómo declarar una lista polimórfica que puede contener cualquier cosa. A '[Data.Dynamic.Dynamic] '¿entonces? – edwardw

+0

Recomendaría no usar 'Dinámico', sino que haré una clase de tipo con las propiedades que los elementos deben tener, y luego usaré un tipo existencial para ocultar el tipo real de los elementos. Puedo elaborar si quieres. – augustss

11

Esto suena como una pregunta bastante básica, por lo que voy a dar una respuesta aún más básica que cualquier otra persona. Esto es lo que es casi siempre la solución correcta:

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

Entonces, si quieres un atributo que envuelve una Int, ese atributo tendría tipo Attribute Int, o un atributo que se ajusta un Bool tendría tipo Attribute Bool, etc.Puede crear estos atributos con valores de cualquier tipo; por ejemplo, podemos escribir

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

para crear un valor de tipo Attribute (Tree Int).

20

Agregando a la respuesta de bdonlan: En lugar de GADTs, también puede utilizar existential 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 

Esto es básicamente equivalente a la solución GADT de bdonlan, pero no impone la elección de la estructura de datos en la que - se puede utilizar un Map lugar de una lista, por ejemplo:

import qualified Data.Map as M 

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

El bit data AnyFoo = forall a. Foo a => AnyFoo a también se puede escribir en notación GADT como:

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

Sí, este es el camino. – augustss

2

Si sus datos necesitan ser finalmente un tipo específico, podría usar Convertible con GADT. Como consumidor, solo está interesado en el tipo de datos que necesita consumir.

{-# 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 

No es muy difícil derivar casos functor, comonad y monad para esto. Puedo publicarlos si estás interesado.

Cuestiones relacionadas