我有一个图书馆项目,很少有专门的单形容器(大多数是集合,也有一些类似地图)。它们都共享基本的Set函数(空,单例,插入,并集等),但也有其他特定于值的函数。其中一些只是Data.Set的包装器,但其他人需要更高效的实现(如有限位集)。我有两个问题:
后缀每个这样的函数(emptyT,singletonU)的优缺点是什么?将所有单态容器函数作为后缀以更容易区分它们和多态函数是否是一个很好的通用做法?
以下是一些示例代码。
import qualified Data.Set as S
data T = ...
newtype TSet = TSet { unpack :: S.Set T }
emptyT = TSet S.empty
singletonT = TSet . S.singleton
这是包装多态容器的正确方法吗?有没有更聪明的方式继承"继承"功能?我考虑过创建一个自定义的Set类型类,但在阅读了一些帖子后,我发现它只会使事情过于复杂..
答案 0 :(得分:1)
我建议你为每个单形专业化使用一个模块。
考虑模块名称Data.Set.Specialized.Int
,Data.Set.Specialized.Char
等。
尽可能重用Data.Set
中使用的相同名称,不带任何后缀。
将您的模块与import qualified
一起用作Data.Set
。
优点:
如果想要将他们的Data.Set
代码更改为专用变体,只需更改类型就可以了。例如
import qualified Data.Set as S
foo :: S.Set Int -> String
到
import qualified Data.Set.Specialized.Int as SI
foo :: SI.Set -> String
没有触及代码。
类型类别也是可能的,但是需要注意选择所有变体中的常见操作。此外,如果我理解正确,您仍然希望允许某些集合具有其他集合不可用的特殊操作。当然,这些都在假设类型之外。
对于问题2:如果换行是newtype
Data.Set
,则应考虑safe coercions。
module Data.Set.Specialized.Int where
import qualified Data.Set as S
import Data.Coerce
newtype Set = Set { unSet :: S.Set Int }
union :: Set -> Set -> Set
union = coerce S.union