Haskell 是否有一种';任何';在哈斯克尔?

Haskell 是否有一种';任何';在哈斯克尔?,haskell,Haskell,比如,我想定义一个记录属性,如下所示: data Attribute=Attribute{name::String,value::Any} 这当然不是有效的haskell代码。但是有没有一种类型的“Any”基本上说任何类型都可以?还是使用类型变量是唯一的方法 data Attribute a=Attribute{name::String,value::a}数据中有类型Dynamic。Dynamic可以保存任何东西(当然,任何Typeable)。但这样做很少是正确的。您试图解决的问题是什么?一般来

比如,我想定义一个记录属性,如下所示:

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

这当然不是有效的haskell代码。但是有没有一种类型的“Any”基本上说任何类型都可以?还是使用类型变量是唯一的方法


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

数据中有类型
Dynamic
。Dynamic可以保存任何东西(当然,任何
Typeable
)。但这样做很少是正确的。您试图解决的问题是什么?

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

人们通常会做的是:

  • 用于制作包含特定typeclass元素的列表,如中所示:

    data FooWrap where
        FooWrap :: Foo a => a -> FooWrap
    type FooList = [FooWrap]
    
    使用这种方法,您不知道元素的具体类型,但您知道可以使用
    Foo
    typeclass的元素来操纵它们

  • 创建一个类型以在列表中包含的特定混凝土类型之间切换:

    data FooElem = ElemFoo Foo | ElemBar Bar
    type FooList = [FooElem]
    
    这可以与方法1结合起来创建一个列表,该列表可以保存属于固定类型类集合之一的元素

  • 在某些情况下,构建操作函数列表可能会有所帮助:

    type FooList = [Int -> IO ()]
    
    这对于事件通知系统之类的系统很有用。在向列表中添加元素时,您可以将其绑定到一个函数中,该函数可以执行您以后想要执行的任何操作

  • 使用
    Data.Dynamic
    (不推荐!)作为欺骗。然而,这并不能保证一个特定的元素可以被操纵,因此应该首选上述方法


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

    data Attribute a = Attribute { name :: String, value :: a }
    
    然后,如果您想要一个包装
    Int
    的属性,该属性将具有类型
    属性Int
    ,或者包装
    Bool
    的属性将具有类型
    属性Bool
    ,等等。您可以使用任何类型的值创建这些属性;例如,我们可以写作

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

    要创建类型为
    Attribute(Tree Int)

    的值,请添加到bdonlan的答案:代替GADTs,您还可以使用:

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

    import qualified Data.Map as M
    
    mFoo :: M.Map String AnyFoo
    mFoo = M.fromList [("a", AnyFoo SomeFoo), ("b", AnyFoo SomeBar)]
    
    所有a的
    数据AnyFoo=。Foo a=>AnyFoo a
    位也可以用GADT表示法写成:

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

    如果您的数据最终需要成为一种特定类型,您可以使用可转换的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实例并不困难。如果您感兴趣,我可以发布它们。

    如果您希望
    name
    具有任何类型,您应该使用类型变量。但是:你确定一个人的名字应该有任何类型吗?可能(例如)作为
    名称的
    Int
    -值或作为名称的
    Bool
    -值不正确@phynfo,我使用的样本是假设的。我只是把它修改得更一般一些。如果你真的需要的话,在模板Haskell级别上翻译这种泛型可能会更好。嗨@augustss,一个是我正在寻找Erlang的any()类型的等价物,以便翻译一些代码。另一个问题是,我想知道如何声明一个可以容纳任何内容的多态列表。A
    [Data.Dynamic.Dynamic]
    那么?我建议不要使用
    Dynamic
    ,而是使用元素需要的属性创建一个类型类,然后使用存在类型隐藏元素的实际类型。如果你愿意,我可以详细说明。谢谢你的详尽回答,@bdonlan。这就是我要找的。注意--请注意,
    Any
    类型在具有子类型的语言中非常有用——一个常见的例子是位于OOP继承层次结构顶部的
    对象
    类。Haskell没有真正的子类型概念,因此这个概念在这里基本上是无用的。
    任何
    (以及所有a.a
    )在以另一种方式存储类型并通过
    unsafeccoerce
    将其展开时可能是有用的,无论出于何种原因,您都不能使用
    可键入的
    。当然,在99%的情况下,这意味着设计喊着“无法形容的恐怖”。@n.m.
    Dublist
    只是一个类型同义词,因此它的使用方式与
    [FooWrap]
    完全相同。
    {-# 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