比如,我想定义一个记录属性,如下所示: data Attribute = Attribute {name :: String, value :: Any} 这当然不是有效的haskell代码。但是有没有一个类型'Any'基本上说任何类型都可以?或者使用类型变量是唯一的方法? data Attribute a = Attribute {name :: String, value :: a}
{-# 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
{-# 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
6条答案
按热度按时间euoag5mw1#
一般来说,
Any
类型不是很有用。如果您创建了一个可以容纳任何内容的多态列表,那么您可以对列表中的类型做些什么呢?答案当然是什么也不做-您无法保证这些元素之间存在任何公共操作。通常会做的是:
1.使用GADTs创建一个列表,该列表可以包含特定类型类的元素,如下所示:
使用这种方法,您不知道元素的具体类型,但知道可以使用
Foo
类型类的元素来操作它们。1.创建类型以在列表中包含的特定具体类型之间切换:
这可以与方法1结合起来创建一个列表,该列表可以保存属于固定类型类集合之一的元素。
1.在某些情况下,构建一个操作函数列表会很有帮助:
这对于事件通知系统很有用,在向列表添加元素时,你可以将它绑定到一个函数中,这个函数可以执行你以后想要做的任何操作。
1.使用
Data.Dynamic
(不推荐!)作为一种欺骗手段。然而,这并不能保证一个特定的元素可以被操纵,因此以上方法应该是首选。vyswwuz22#
博杜伦的回答中还补充了一点:除了GADT,您还可以使用existential types:
这基本上等同于bdonlan的GADT解决方案,但不会强制您选择数据结构-您可以使用
Map
代替列表,例如:data AnyFoo = forall a. Foo a => AnyFoo a
位也可以用GADT表示法写成:agxfikkp3#
Data.Dynamic
中有一个类型Dynamic
,它可以容纳任何内容(当然,可以容纳Typeable
中的任何内容),但这通常不是正确的方法,您要解决的问题是什么?ffdz8vbo4#
这听起来像是一个非常基本的问题,所以我会给予一个比任何人都更基本的答案。下面是几乎总是正确的答案:
然后,如果需要 Package
Int
的属性,则该属性的类型应为Attribute Int
, PackageBool
的属性的类型应为Attribute Bool
,以此类推。可以使用任何类型的值创建这些属性;比如我们可以写来创建
Attribute (Tree Int)
类型的值。ao218c7q5#
如果你的数据最终需要成为一个特定的类型,你可以使用Convertible和GADT,因为作为消费者,你只对你需要消费的数据类型感兴趣。
这不是很难推导出函子,共单元素和单子示例。我可以张贴他们,如果你有兴趣。
sycxhyv76#
丹尼尔·瓦格纳的回答是正确的:几乎在90%的情况下使用多态类型是你所需要的全部。2所有其他的回答对剩下的10%的情况都是有用的,但是如果你仍然没有很好的多态知识来问这个问题,那么理解GADT或存在类型将是极其复杂的......我的建议是“尽可能保持简单”。