限制类型参数在数据声明中可以采用的类型

Limiting the Types that a Type Paramater Can Take On in Data Declarations

我知道 Haskell 具有参数化数据类型:

data Maybe a = Nothing | Just a

但是有没有办法限制 a 可以表示的类型?特别是,我想创建一个类型

data Tag a = Tag a

这样 a 可以采用 类型 TagPrimitive 类型 TagComplex (但是,我不希望 a 的类型可能是 IntegerString 或在我的程序中没有意义的类型)。

这可能吗?

您可以使用通常的单例方法:

{-# language GADTs #-}

data Taggable a where
  Primitive : Taggable TagPrimitive
  Complex : Taggable TagComplex

然后定义Tag

data Tag a where
  Tag : Taggable a -> a -> Tag a

然后当你调度给定 Tag 值的 a 时,你可以只匹配单例:

 f :: Tag a -> T
 f (Tag Primitive x) = ... - here, you know x :: TagPrimitive
 f (Tag Complex x) = ... -- here, you know x :: TagComplex

或者,正如@rampion 在评论中提到的那样,您可以将 Taggable 折叠成 Tag,留下

data Tag a where
  Primitive :: TagPrimitive -> Tag TagPrimitive
  Complex :: TagComplex -> Tag TagComplex

您提议的 Tag 类型有点奇怪:它是一个参数化类型,可以专门用于 可以包含 TagPrimitive 的类型s 可能专门用于 包含 TagComplex 的类型。但这似乎有点毫无意义:我们已经有两个专门的类型 TagPrimitiveTagComplex 来服务这两个角色。

相反,我建议您真正想要的是可以包含任一类型值的单一类型。为此,我建议准备一个新的总和类型:

data Tag = Primitive TagPrimitive | Complex TagComplex
    deriving (Eq, Ord, Read, Show)

对于早期原型,您可能会使用规范求和类型 Either,如

type Tag = Either TagPrimitive TagComplex

但我怀疑随着程序的增长,这将变成 increasingly bad choice