Haskell中如何不新增模板参数封装数据类型的通用模式
你给出的Sequence定义在标准Haskell中无法直接通过编译,因为UnFold构造器里的类型参数b没有出现在Sequence的类型参数列表中,属于未绑定的存在类型变量。要实现你要的封装效果,只需要开启ExistentialQuantification扩展修正定义,不需要额外新增顶层类型参数:
{-# LANGUAGE ExistentialQuantification #-} data Sequence a = FirstThen a (Sequence a) | Repeating a | forall b. UnFold b (b -> b) (b -> a)
这种写法会把UnFold内部的状态类型b完全隐藏,编译器会在你构造UnFold值的时候自动校验初始状态、步进函数、投影函数的类型匹配,构造完成后外部完全不需要感知b的存在,所有操作都能保证类型安全,不会出现你担心的非法访问内部状态导致的类型错误。
接下来可以给这个类型封装通用的操作接口,不需要针对不同构造器做特殊处理:
- 实现
Functor类型类,支持通用映射操作,适用所有构造器的Sequence:
instance Functor Sequence where fmap f (FirstThen x s) = FirstThen (f x) (fmap f s) fmap f (Repeating x) = Repeating (f x) fmap f (UnFold s step proj) = UnFold s step (f . proj)
- 实现通用的步进函数,统一所有序列的遍历逻辑:
next :: Sequence a -> (a, Sequence a) next (FirstThen x s) = (x, s) next (Repeating x) = (x, Repeating x) next (UnFold s step proj) = (proj s, UnFold (step s) step proj)
基于这个步进函数可以实现任意通用操作,比如取前n个元素、折叠、过滤等,完全不需要关心序列底层是哪种实现:
take :: Int -> Sequence a -> [a] take 0 _ = [] take n s = let (x, s') = next s in x : take (n-1) s'
这个方案的适用性远高于针对UnFold的特殊惰性映射,所有对Sequence的操作都可以通用复用,同时完全保证类型安全。
内容的提问来源于stack exchange,提问作者user13507303
相关产品推荐
相关产品推荐

