В настоящее время мы используем расширение DataKinds
:
{-# LANGUAGE DataKinds, KindSignatures, ScopedTypeVariables #-}
-- N is a type, but is also a kind now
-- Zero, Succ Zero, ... are values, but are also type-level values of
-- kind N
data N = Zero | Succ N
-- (We could import Proxy the library, instead)
data Proxy (n :: N) = Proxy
-- Now n is restricted to kind N
class Nat (n :: N) where
toInt :: proxy n -> Int
instance Nat Zero where
toInt _ = 0
instance (Nat n) => Nat (Succ n) where
toInt _ = 1 + toInt (undefined :: Proxy n)
Затем мы можем использовать toInt (Proxy :: Proxy (Succ Zero))
.Вместо этого, toInt (Proxy :: Proxy (Succ Int))
вызовет ошибку типа, как хотелось бы.
Лично я бы также заменил прокси на более современные вещи, такие как AllowAmbiguousTypes
и TypeApplications
, чтобы удалить неиспользуемый аргумент.
{-# LANGUAGE DataKinds, KindSignatures, ScopedTypeVariables,
AllowAmbiguousTypes, TypeApplications #-}
data N = Zero | Succ N
-- Now n is restricted to kind N
class Nat (n :: N) where
toInt :: Int
instance Nat Zero where
toInt = 0
instance (Nat n) => Nat (Succ n) where
toInt = 1 + toInt @n
Используйте это как toInt @(Succ Zero)
.Синтаксис toInt @n
выбирает n
в классе типов.Он не соответствует никакому значению, которым обмениваются во время выполнения, только аргумент уровня типа, который существует во время компиляции.
Использование
type Foo = Succ Int
также выдает ошибки по желанию:
• Expected kind ‘N’, but ‘Int’ has kind ‘*’
• In the first argument of ‘Succ’, namely ‘Int’
In the type ‘Succ Int’
In the type declaration for ‘Foo’