【发布时间】:2011-08-02 06:46:58
【问题描述】:
感谢newtype 和GeneralizedNewtypeDeriving 扩展,可以轻松定义不同的轻量级类型:
newtype PersonId = PersonId Int deriving (Eq, Ord, Show, NFData, ...)
newtype GroupId = GroupId Int deriving (Eq, Ord, Show, NFData, ...)
这允许类型系统确保 PersonId 不会在预期 GroupId 的地方被意外使用,但仍然从 Int 继承选定的类型类实例。
现在可以简单地将PersonIdSet 和GroupIdSet 定义为
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
这是类型安全的,因为 map 是由键类型参数化的,而且 Set.member 操作是多态的,所以我不需要定义每个 id 类型的变体,例如 personIdSetMember 和 @987654332 @(以及我可能想要使用的所有其他集合操作)
...但是如何以与上述示例类似的方式分别使用更有效的IntSets 来代替PersonIdSet 和GroupIdSet?有没有一种简单的方法,无需将整个 Data.IntSet API 包装/复制为类型类?
【问题讨论】:
-
IIRC 编号但我想你可以使用模板 Haskell。
-
你写了 cmets 说什么应该输入检查,什么不应该。你有没有试过编译,哪里的结果和你预期的不一样?
-
@jmg,我只是想确定一下,结果符合预期。 GHC 发出的实际错误是
Couldn't match expected type 'PersonId' with actual type 'GroupId' -
那么,您问题中的代码提供了您要询问的所有属性,不是吗?还是你想要别的?
-
@jmg: 是的,但我希望这些属性具有更高效的
IntSet而不是通用的Set。