如何“新类型” INTSET? [英] How to "newtype" IntSet?
问题描述
感谢 newtype
和 GeneralizedNewtypeDeriving
扩展,您可以毫不费力地定义不同的轻量类型:
newtype PersonId = PersonId Int派生(Eq,Ord,Show,NFData,...)
newtype GroupId = GroupId Int派生(Eq,Ord,Show,NFData,...)
允许类型系统确保在 GroupId
预期的情况下不会偶然使用 PersonId
,但仍会从<$ c继承选定的类型实例现在人们可以简单地定义 PersonIdSet
和<$ c
$ import $ Data Set
导入合格的数据Set as Set
类型PersonIdSet = Set PersonId
类型GroupIdSet = Set GroupId
noGroups :: GroupIdSet
noGroups = Set.empty
- 不应该类型检查
foo = PersonId 123`Set.member` noGroups
- should type-c heck
bar = GroupId 123`Set.member` noGroups
这是类型安全的,因为map是通过键类型参数化的,并且 Set.member
操作是多态的,所以我不需要定义每个id类型的变体,比如 personIdSetMember
和 groupIdSetMember
(以及其他所有可能要使用的设置操作)
...但我怎样才能使用效率更高的 IntSet
s代替 PersonIdSet
和 GroupIdSet
分别类似于上面的示例吗?有没有一种简单的方法,无需将整个Data.IntSet API作为类型类包装/复制?
我认为如你所说,你必须包装 IntSet
。但是,不是单独定义每个ID类型,您可以引入一个幻像类型来创建 ID
s和 IDSet
{ - #LANGUAGE GeneralizedNewtypeDeriving# - }
导入 (Eq,Ord,Show,Num)Data.IntSet作为IntSet
导入Data.IntSet(IntSet)
newtype ID a = ID {unID :: Int}
newtype IDSet a = IDSet {unIDSet :: IntSet}
deriving(Eq,Ord,Show)
$ b $ null :: IDSet a - > Bool
null = IntSet.null。 unIDSet
成员:: ID a - > IDSet a - > Bool
成员i = IntSet.member(unID i)。 unIDSet
empty :: IDSet a
empty = IDSet $ IntSet.empty
singleton :: ID a - > ID设置
singleton = IDSet。 IntSet.singleton。 unID
insert :: ID a - > IDSet a - > IDSet a
插入i = IDSet。 IntSet.insert(unID i)。 unIDSet
delete :: ID a - > IDSet a - > IDSet a
删除i = IDSet。 IntSet.delete(unID i)。因此,假设你有一个 Person
>类型和组类型,您可以执行:
type PersonID = ID Person
类型PersonIDSet = IDSet Person
类型GroupID = ID Group
类型GroupIDSet = IDSet Group
Thanks to newtype
and the GeneralizedNewtypeDeriving
extension, one can define distinct lightweight types with little effort:
newtype PersonId = PersonId Int deriving (Eq, Ord, Show, NFData, ...)
newtype GroupId = GroupId Int deriving (Eq, Ord, Show, NFData, ...)
which allows the type-system to make sure a PersonId
is not used by accident where a GroupId
was expected, but still inherit selected typeclass instances from Int
.
Now one could simply define PersonIdSet
and GroupIdSet
as
import Data.Set (Set)
import qualified Data.Set as Set
type PersonIdSet = Set PersonId
type GroupIdSet = Set GroupId
noGroups :: GroupIdSet
noGroups = Set.empty
-- should not type-check
foo = PersonId 123 `Set.member` noGroups
-- should type-check
bar = GroupId 123 `Set.member` noGroups
which is type safe, since map is parametrized by the key-type, and also, the Set.member
operation is polymorphic so I don't need to define per-id-type variants such as personIdSetMember
and groupIdSetMember
(and all other set-operations I might want to use)
...but how can I use the more efficient IntSet
s instead for PersonIdSet
and GroupIdSet
respectively in a similiar way to the example above? Is there a simple way w/o having to wrap/replicate the whole Data.IntSet API as a typeclass?
解决方案 I think you have to wrap IntSet
as you said. However, rather than defining each ID type separately, you can introduce a phantom type to create a family of ID
s and IDSet
s that are compatible with one another:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import qualified Data.IntSet as IntSet
import Data.IntSet (IntSet)
newtype ID a = ID { unID :: Int }
deriving ( Eq, Ord, Show, Num )
newtype IDSet a = IDSet { unIDSet :: IntSet }
deriving ( Eq, Ord, Show )
null :: IDSet a -> Bool
null = IntSet.null . unIDSet
member :: ID a -> IDSet a -> Bool
member i = IntSet.member (unID i) . unIDSet
empty :: IDSet a
empty = IDSet $ IntSet.empty
singleton :: ID a -> IDSet a
singleton = IDSet . IntSet.singleton . unID
insert :: ID a -> IDSet a -> IDSet a
insert i = IDSet . IntSet.insert (unID i) . unIDSet
delete :: ID a -> IDSet a -> IDSet a
delete i = IDSet . IntSet.delete (unID i) . unIDSet
So, assuming you have a Person
type, and a Group
type, you can do:
type PersonID = ID Person
type PersonIDSet = IDSet Person
type GroupID = ID Group
type GroupIDSet = IDSet Group
这篇关于如何“新类型” INTSET?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!