考虑以下数据类型:
data Get (statusCode :: Nat)
实际上,它是来自servant 的简化类型构造函数,然后在类型级API 中使用,如下所示:
type API = "users" :> Verb 'GET 200 '[JSON] [User]
出于我们的目的,我们可以将 API 缩减为
type API = Get 200
现在,对状态码的限制Nat
过于宽松,允许定义一个不存在的 HTTP 状态码:
type API = Get 999
因此,问题是:有没有办法限制可以应用于Get
类型构造函数的自然集合?
尝试了什么
为了清楚起见,我将省略代码示例中的所有编译指示和导入。
一种不同的statusCode
一种明显的解决方法是为状态代码定义一个单独的 ADT,并使用它来代替Nat
使用数据类型提升。
data StatusCode = HTTP200 | HTTP201 | HTTP202
data Get (statusCode :: StatusCode)
但是,这是一个重大更改,需要升级主要版本并重写所有用户的定义。我怀疑限制代码的好处是否值得。
数据类型上下文
这个扩展允许对我们的类型变量有一个直接的约束
data IsStatusCode statusCode => Get (statusCode :: Nat)
但它要求用户将约束添加到他们的所有声明中。再次,一个突破性的变化。此外,DatatypeContexts
已弃用。
类型家庭
我们可以Get'
使用类型族从下面的示例有条件地创建,但由于某种原因,声明类型别名会很高兴地编译。为了得到一个错误,我们需要构造一个这种类型的值,这也是一个突破性的变化。
data Get' (statusCode :: Nat) = Get
type family Get x where
Get x = If (x <=? 600) (Get' x) (TypeError (Text "Invalid Code"))
type API1 = Get 200
type API2 = Get 999 -- compiles.
api :: Get 999 -- doesn't compile.
api = Get