Haskell中能否用类型参数化追踪配对的创建集合且不存储集合?
问题
我定义了类型A,以及表示A配对的类型(A, A),还为这个配对类型实现了一个智能构造函数——它接收一个A的集合、两个A类型的值,先执行校验(比如确保值属于该集合),校验通过后返回这两个值组成的配对。
现在我希望追踪创建配对时所用的集合,但不想在每个配对实例中存储该集合。考虑用A的集合对应的类型来参数化配对类型,这种方案在Haskell中可行吗?
补充说明:
假设有两个整数列表[1, 2, 3]和[1, 2, 4],用第一个列表搭配1和2,第二个列表搭配1和2,都会成功返回配对(1, 2),但当前实现里生成的配对丢失了创建时所用列表的上下文。我的目标是调整类型定义,让生成的配对能根据创建时的列表区分,但不在配对值中存储该列表。
更新:我猜测DataKinds扩展可能能满足需求。
解决方案
完全可行,DataKinds正是解决这类问题的合适工具——它能把值提升为类型层面的标记,让我们在类型里追踪集合信息,同时不在运行时存储集合本身。
下面是具体的实现示例:
首先需要启用必要的扩展:
{-# LANGUAGE DataKinds #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE FlexibleContexts #-}
- 利用
DataKinds将值层面的列表直接提升为类型层面的标记:
-- 无需额外定义类型家族,DataKinds直接支持将列表值提升为类型符号
- 定义带类型参数的配对类型,用提升后的集合类型作为参数:
-- Pair s a 中,s是类型层面的集合标记(比如 '[1,2,3]),a是元素类型 data Pair s a where Pair :: a -> a -> Pair s a
- 实现智能构造函数,在运行时校验元素合法性,同时绑定对应的类型标记:
-- 辅助函数:检查元素是否在列表中 elemList :: Eq a => a -> [a] -> Bool elemList x = any (== x) -- 智能构造函数:接收值层面的集合、两个元素,返回带对应类型标记的Pair mkPair :: (Eq a, s ~ 'xs) => [a] -> a -> a -> Maybe (Pair s a) mkPair xs x y | elemList x xs && elemList y xs = Just (Pair x y) | otherwise = Nothing
- 使用示例:
-- 定义两个值层面的列表,它们会被自动提升为不同的类型标记 list1 :: [Int] list1 = [1,2,3] list2 :: [Int] list2 = [1,2,4] -- 创建配对,类型会自动推导为对应的标记 pair1 :: Maybe (Pair '[1,2,3] Int) pair1 = mkPair list1 1 2 pair2 :: Maybe (Pair '[1,2,4] Int) pair2 = mkPair list2 1 2
这样,pair1和pair2的类型是完全不同的(分别对应'[1,2,3]和'[1,2,4]这两个类型标记),但它们的运行时值都是Pair 1 2,没有存储原列表。类型系统会帮我们严格区分这两个配对的来源集合,同时不会带来任何运行时的额外开销。
需要注意的是,这种方式要求集合是编译期可确定的常量,如果你的集合是运行时动态生成的,这种方案就不适用了——但如果是静态集合,DataKinds的方案完全符合你的需求。
内容的提问来源于stack exchange,提问作者Seeker
相关产品推荐
相关产品推荐

