Ответ 1
Я начну с решения, а затем обсужу другие возможности (которые, как представляется, не были развернуты):
{-# LANGUAGE TypeOperators, TypeInType, GADTs #-}
import GHC.TypeLits (Nat, type (<=))
import Data.Proxy (Proxy(..))
import Data.Kind (type (*))
data Get (statusCode :: Nat) :: (statusCode <= 600) => *
type API1 = Get 900 -- compiles
type API2 = Get 200 -- compiles
api1 :: Proxy (Get 900) -- doesn't compile
api1 = Proxy
api2 :: Proxy (Get 200) -- compiles
api2 = Proxy
Нет семейств типа, которые вам нужны, и не нужно никогда не опускаться до уровня ценности. Однако синонимы типов будут скомпилированы. Использование синонима типа недействительного Get
приведет к сбою времени компиляции на сайте использования. Я считаю, что это так же хорошо, как вы можете надеяться. Пожалуйста, дайте мне знать, если что-то неясно.
Далее, только некоторые мысли о других подходах:
DatatypeContexts
Этот никогда не будет работать: помимо устаревания все это добавляет ограничение к конструкторам. Вы специально указали, что не хотите создавать что-либо типа, поэтому это бессмысленно. Новый синтаксис GADT устраняет эту неоднозначность - ограничения теперь явно связаны с конструкторами данных или типов.
Тип семейства и TypeError
Я считаю, что это должно работать, и не нужно строить значение типа. (Итак, должно быть хорошо:)
data Get' (statusCode :: Nat)
type family Get x where
Get x = If (x <=? 600) (Get' x) (TypeError (Text "Invalid Code"))
api2 :: Proxy (Get 200) -- should compile.
api2 = Proxy
api1 :: Proxy (Get 999) -- shouldn't compile, but currently does
api1 = Proxy
Я мотивирован полагать, что это должно работать на основе example в этом тике. Что-то здесь явно не ведет себя так, как должно: даже если я заменю вызов функции типа If
самой TypeError
, все равно ничего не запускается - кажется, что TypeErrors
, которые не являются верхним уровнем, все еще вызывают некоторые проблемы.
С другой стороны, я действительно не знаю, как должно выглядеть поведение синтаксисов TypeError
в type
, хотя я склонен сказать, что type API1 = Get 999
не следует компилировать, так как type API1 = TypeError (Text "error")
этого не делает.