Haskell 泛型:具有实例的类型的运行时ADT
使用Haskell/GHC是否可以提取一个代数数据类型,用Eq和Ord实例表示所有类型?这可能需要泛型、可键入等 我想要的是:Haskell 泛型:具有实例的类型的运行时ADT,haskell,Haskell,使用Haskell/GHC是否可以提取一个代数数据类型,用Eq和Ord实例表示所有类型?这可能需要泛型、可键入等 我想要的是: data Data_Eq_Ord = Data_String String | Data_Int Int | Data_Bool Bool | ... deriving (Eq, Ord) 对于已知具有Eq和Ord实例的所有类型。如果它使解决方案更容易,我们可以将我
data Data_Eq_Ord = Data_String String
| Data_Int Int
| Data_Bool Bool
| ...
deriving (Eq, Ord)
对于已知具有Eq和Ord实例的所有类型。如果它使解决方案更容易,我们可以将我们的范围限制在Ord实例上,因为Eq是Ord隐含的。但如果知道约束相交是否可能,这将是一个有趣的问题
这种数据类型很有用,因为它可以在需要Eq和Ord约束的地方使用它,并在运行时进行模式匹配以优化类型
我需要它来实现一个通用映射键值,其中键是这种类型的,在文档索引库中,键及其类型在运行时是已知的。这个图书馆是免费的。目前,我通过定义一个数据DocIndexKey
和一个字段key
类来解决这个问题,但这并不完全令人满意,因为它需要样板文件,并且不能涵盖所有合法的候选者
任何解决这种情况的好办法都是值得欢迎的。出于某些原因,我倾向于避免使用模板Haskell。好吧,它不是ADT,但这确实有效:
data Satisfying c = forall a. c a => Satisfy a
class (l a, r a) => And l r a where
instance (l a, r a) => And l r a where
ex :: [Satisfying (Typeable `And` Show `And` Ord)]
ex = [ Satisfy (7 :: Int)
, Satisfy "Hello"
, Satisfy (5 :: Int)
, Satisfy [10..20 :: Int]
, Satisfy ['a'..'z']
, Satisfy ((), 'a')]
-- An example of use, with "complicated" logic
data With f c = forall a. c a => With (f a)
-- vvvvvvvvvvvvvvvvvvvvvvvvvv QuantifiedConstraints chokes on this, which is probably a bug...
partitionTypes :: (forall a. c a => TypeRep a) -> [Satisfying c] -> [[] `With` c]
partitionTypes rep = foldr go []
where go (Satisfy x) [] = [With [x]]
go x'@(Satisfy (x :: a)) (xs'@(With (xs :: [b])) : xss) =
case testEquality rep rep :: Maybe (a :~: b) of
Just Refl -> With (x : xs) : xss
Nothing -> xs' : go x' xss
main :: IO ()
main = traverse_ (\(With xs) -> print (sort xs)) $ partitionTypes typeRep ex
精疲力竭要困难得多。也许有了一个插件,你就可以让GHC来做这件事了,但为什么还要麻烦呢?我不相信GHC真的试图追踪它所看到的类型。特别是,您必须扫描项目中的所有模块及其依赖项,甚至那些尚未被包含类型定义的模块加载的模块。你必须从头开始实施。正如这个答案所示,我非常怀疑你是否真的能够将这种穷尽性应用于任何你仅仅以开放宇宙的现状无法做到的事情。我的第一个想法是你是否可以使用
数据。可键入的和/或数据。动态的对于这一点……泛型和可键入的绝对不会让你达到目的。TypeClass在设计上是开放的,也就是说,以后总是可以添加更多的实例。因此,Data\u Eq\u Ord
永远不能保证是完整的。也许这是可能的与TH得到所有的实例,目前在范围内,但我甚至不确定我想说,你最好的选择可能是使用TypeRep
s作为键,进入一个概念上无限的、懒惰的MemoTrie
结构或其他东西。当编译终止时,不能再添加实例,因此在某个时刻编译器知道在整个程序中定义了哪些Ord实例。也就是说,模块编译序列确实存在一个挑战。这是一个有趣的主题,虽然它不是我希望找到的解决方案,但它展示了非常先进的类型技术,可以用于类似的情况。非常感谢你!