haskell中有'Any'类型吗?

edw*_*rdw 42 haskell

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

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

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

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

bdo*_*lan 68

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

通常做的是:

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

    data FooWrap where
        FooWrap :: Foo a => a -> FooWrap
    type FooList = [FooWrap]
    
    Run Code Online (Sandbox Code Playgroud)

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

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

    data FooElem = ElemFoo Foo | ElemBar Bar
    type FooList = [FooElem]
    
    Run Code Online (Sandbox Code Playgroud)

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

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

    type FooList = [Int -> IO ()]
    
    Run Code Online (Sandbox Code Playgroud)

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

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

  • 注意 - 注意`Any`类型在*带有子类型的语言中非常有用* - 一个常见的例子是OOP继承层次结构顶部的`Object`类.Haskell没有真正的子类型概念,所以这个概念在这里几乎没用. (17认同)
  • +1表示"正确"的方法. (2认同)
  • 当你以其他方式存储类型并用`unsafeCoerce`打开它时,`any`(以及`forall a.a`)可能很有用,你也不能因为某种原因使用`Typeable`.当然,在99%的情况下,这意味着设计会"无法形容的恐怖". (2认同)
  • @nm`FooList`只是一个类型同义词,所以它的使用方式与`[FooWrap]`完全相同. (2认同)

Mik*_*kov 21

添加到bdonlan的答案:您也可以使用存在类型代替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
Run Code Online (Sandbox Code Playgroud)

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

import qualified Data.Map as M

mFoo :: M.Map String AnyFoo
mFoo = M.fromList [("a", AnyFoo SomeFoo), ("b", AnyFoo SomeBar)]
Run Code Online (Sandbox Code Playgroud)

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

data AnyFoo where
  AnyFoo :: Foo a => a -> AnyFoo
Run Code Online (Sandbox Code Playgroud)


aug*_*tss 13

有类型DynamicData.Dynamic,可容纳任何东西(当然,任何东西Typeable).但这很少是正确的方法.你试图解决的问题是什么?


Dan*_*ner 12

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

data Attribute a = Attribute { name :: String, value :: a }
Run Code Online (Sandbox Code Playgroud)

然后,如果你想要一个包装an的属性Int,该属性将具有类型Attribute Int,或者包含a的属性Bool将具有类型Attribute Bool等.您可以使用任何类型的值创建这些属性; 例如,我们可以写

testAttr = Attribute { name = "this is only a test", value = Node 3 [] }
Run Code Online (Sandbox Code Playgroud)

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