【问题标题】:Is there a type 'Any' in haskell?haskell 中是否有“任何”类型?
【发布时间】:2011-06-25 17:37:20
【问题描述】:

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

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

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

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

【问题讨论】:

  • 如果你想让name 有任何类型,你应该使用类型变量。但是:你确定一个人的name 应该有任何类型吗?可能(例如)Int-value 作为 nameBool-value 作为名称不正确!?
  • @phynfo,我使用的样本是假设的。我只是将其修改为更通用。
  • 如果你真的需要,这种通用性级别可能在 Template Haskell 级别上得到更好的翻译。

标签: haskell


【解决方案1】:

一般来说,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(不推荐!)作弊。但是,这并不能保证完全可以操纵特定元素,因此应首选上述方法。

【讨论】:

  • 感谢@bdonlan 的详尽回答。这就是我要找的。​​span>
  • 注: -- 请注意Any 类型在具有子类型化的语言 中非常有用——一个常见的例子是位于OOP 继承层次结构顶部的Object 类。 Haskell 没有真正的子类型概念,所以这个概念在这里几乎没有用处。
  • Any(以及forall a. a)在您以其他方式存储类型并通过unsafeCoerce 解包并且无论出于何种原因都不能使用Typeable 时可能很有用。当然,在 99% 的情况下,这意味着设计呼唤着“无法形容的恐怖”。
  • @n.m. FooList 只是一个类型的同义词,所以它的使用方式与[FooWrap] 完全相同。
【解决方案2】:

添加到 bdonlan 的答案:除了 GADT,您还可以使用existential 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 解决方案,但不会将数据结构的选择强加给您 - 您可以使用 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

【讨论】:

    【解决方案3】:

    Data.Dynamic 中的 Dynamic 类型可以容纳任何东西(嗯,任何东西 Typeable)。但这很少是正确的方法。您要解决的问题是什么?

    【讨论】:

    • 嗨@augustss,一个是我正在寻找相当于Erlang 的any() 类型来翻译一些代码。另一个是我想知道如何声明一个可以容纳任何东西的多态列表。那么[Data.Dynamic.Dynamic]
    • 我建议不要使用Dynamic,而是使用元素需要具有的属性创建一个类型类,然后使用存在类型来隐藏元素的实际类型。如果你愿意,我可以详细说明。
    【解决方案4】:

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

    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) 类型的值。

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-12-08
        • 2011-04-19
        • 1970-01-01
        • 1970-01-01
        • 2021-03-25
        • 1970-01-01
        相关资源
        最近更新 更多