haskell中有'Any'类型吗?

时间:2011-06-25 17:37:21

标签: haskell

说,我想像这样定义一个记录属性:

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

这当然不是有效的haskell代码。但是有一种类型'任何',基本上说任何类型都会这样做?或者只是使用类型变量?

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

5 个答案:

答案 0 :(得分:66)

一般来说,Any类型不是很有用。考虑一下:如果你创建了一个可以容纳任何内容的多态列表,你可以对列表中的类型做些什么?答案当然不算什么 - 你不能保证这些元素有任何共同的操作。

通常做的是:

  1. 使用GADTs创建一个可以包含特定类型类的元素的列表,如:

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

    使用这种方法,您不知道元素的具体类型,但您知道可以使用Foo类型类的元素来操作它们。

  2. 创建一个类型以在列表中包含的特定具体类型之间切换:

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

    这可以与方法1结合使用,以创建一个列表,该列表可以包含一组固定类型类别之一的元素。

  3. 在某些情况下,构建操作函数列表会很有帮助:

    type FooList = [Int -> IO ()]
    

    这对事件通知系统之类的东西很有用。在向列表添加元素时,您可以将其绑定在一个函数中,该函数执行您以后想要执行的任何操作。

  4. 使用Data.Dynamic(不推荐!)作为作弊。但是,这并不保证可以完全操纵特定元素,因此应首选上述方法。

答案 1 :(得分:20)

添加到bdonlan的答案:您可以使用existential types代替GADT:

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

这基本上等同于bdonlan的GADT解决方案,但不会强加您对数据结构的选择 - 您可以使用Map代替列表,例如:

import qualified Data.Map as M

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

data AnyFoo = forall a. Foo a => AnyFoo a位也可以用GADT表示法写成:

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

答案 2 :(得分:12)

来自Dynamic的{​​{1}}类型可以容纳任何内容(好吧,任何Data.Dynamic)。但这很少是正确的方法。你试图解决的问题是什么?

答案 3 :(得分:12)

这听起来像一个非常基本的问题,所以我会给出一个比其他人更基本的答案。这几乎总是正确的解决方案:

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

然后,如果您想要一个包含Int的属性,该属性的类型为Attribute Int,或者包含Bool的属性的类型为Attribute Bool,您可以使用任何类型的值创建这些属性;例如,我们可以写

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

创建Attribute (Tree Int)类型的值。

答案 4 :(得分:2)

如果您的数据最终需要特定类型,您可以将Convertible与GADT一起使用。因为作为消费者,您只对您需要使用的数据类型感兴趣。

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

为此派生仿函数,comonad和monad实例并不是很困难。如果你有兴趣,我可以发布它们。