haskell中是否有“Any”types?
说,我想定义一个logging这样的属性:
data Attribute = Attribute {name :: String, value :: Any}
这当然不是有效的haskell代码。 但有没有一种types的“任何”,基本上说任何types都可以做? 或者是使用typesvariables的唯一方法?
data Attribute a = Attribute {name :: String, value :: a}
一般来说, Any
types都不是很有用。 考虑一下:如果你创build一个可以容纳任何东西的多态列表,你可以用列表中的types做什么? 答案当然不算什么 – 你不能保证这些元素有任何共同的操作。
通常会做的是:
-
使用GADT创build一个可以包含特定typestypes元素的列表,如下所示:
data FooWrap where FooWrap :: Foo a => a -> FooWrap type FooList = [FooWrap]
使用这种方法,您不知道元素的具体types,但是您知道可以使用
Foo
types元素来操作元素。 -
创build一个在列表中包含的特定具体types之间切换的types:
data FooElem = ElemFoo Foo | ElemBar Bar type FooList = [FooElem]
这可以与方法1相结合来创build一个列表,该列表可以包含固定的一组types的元素。
-
在某些情况下,build立一个操作函数列表会很有帮助:
type FooList = [Int -> IO ()]
这对于事件通知系统是非常有用的。 在添加一个元素到列表中的时候,你可以将它绑定到一个函数中,该函数将执行你以后想要做的任何操作。
-
使用
Data.Dynamic
(不推荐!)作为作弊。 然而,这不能保证一个特定的元素可以被操纵,所以上述方法应该是优选的。
添加到bdonlan的答案:而不是GADT,你也可以使用存在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
这基本上等同于bdonlan的GADT解决scheme,但并不强制您select数据结构 – 您可以使用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
data AnyFoo = forall a. Foo a => AnyFoo a
也可以用GADT表示法写成:
data AnyFoo where AnyFoo :: Foo a => a -> AnyFoo
Data.Dynamic
types可以容纳任何东西(好的,任何Typeable
)。 但这很less是正确的做法。 你试图解决什么问题?
这听起来像是一个很基本的问题,所以我会给出一个比其他人更基本的答案。 以下几乎总是正确的解决scheme:
data Attribute a = Attribute { name :: String, value :: a }
然后,如果你想要一个包装一个Int
的属性,那么这个属性的types应该是Attribute Int
,或者一个包装一个Bool
的属性的types是Attribute Bool
,等等。你可以用任何types的值创build这些属性。 例如,我们可以写
testAttr = Attribute { name = "this is only a test", value = Node 3 [] }
创build一个typesAttribute (Tree Int)
。
如果您的数据最终需要特定types,则可以将Convertible与GADT一起使用。 因为作为消费者,你只对你需要使用的数据types感兴趣。
{-# 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 af) = fa unconv (Chain cf) = f $ unconv c conv :: Convertible ab => a -> Conv b conv a = (Conv a convert) totype :: Convertible bc => Conv b -> Conv c totype a = Chain a convert
为此派生函子,共性和单子实例并不是很困难。 如果你有兴趣,我可以发表。