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