说,我想像这样定义一个记录属性:
data Attribute = Attribute {name :: String, value :: Any}
这当然不是有效的haskell代码。但是有一种类型'任何',基本上说任何类型都会这样做?或者只是使用类型变量?
data Attribute a = Attribute {name :: String, value :: a}
答案 0 :(得分:66)
一般来说,Any
类型不是很有用。考虑一下:如果你创建了一个可以容纳任何内容的多态列表,你可以对列表中的类型做些什么?答案当然不算什么 - 你不能保证这些元素有任何共同的操作。
通常做的是:
使用GADTs创建一个可以包含特定类型类的元素的列表,如:
data FooWrap where
FooWrap :: Foo a => a -> FooWrap
type FooList = [FooWrap]
使用这种方法,您不知道元素的具体类型,但您知道可以使用Foo
类型类的元素来操作它们。
创建一个类型以在列表中包含的特定具体类型之间切换:
data FooElem = ElemFoo Foo | ElemBar Bar
type FooList = [FooElem]
这可以与方法1结合使用,以创建一个列表,该列表可以包含一组固定类型类别之一的元素。
在某些情况下,构建操作函数列表会很有帮助:
type FooList = [Int -> IO ()]
这对事件通知系统之类的东西很有用。在向列表添加元素时,您可以将其绑定在一个函数中,该函数执行您以后想要执行的任何操作。
使用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实例并不是很困难。如果你有兴趣,我可以发布它们。