Ответ 1
Во-первых, некоторые импортные и языковые расширения:
{-# LANGUAGE GADTs, TypeInType, RankNTypes, TypeOperators, TypeFamilies, TypeApplications, AllowAmbiguousTypes #-}
import Data.Type.Equality
Теперь мы имеем DataKinds
(или TypeInType
), что позволяет нам продвигать любые данные на уровень типа (со своим видом), поэтому тип нулевые уровни на самом деле заслуживают того, чтобы их можно было определить как регулярные data
(черт возьми, это как раз мотивы, приведенные в предыдущей ссылке на документы GHC!). Ничего не меняется с вашим типом List
, но (:+:)
действительно должен быть закрытым типом семейства (теперь над вещами рода Nat
).
-- A natural number type (that can be promoted to the type level)
data Nat = Z | S Nat
-- A List of length 'n' holding values of type 'a'
data List a n where
Nil :: List a Z
Cons :: a -> List a m -> List a (S m)
type family (+) (a :: Nat) (b :: Nat) :: Nat where
Z + n = n
S m + n = S (m + n)
Теперь, чтобы сделать доказательства для aux
, полезно определить одноэлементные типы для натуральных чисел.
-- A singleton type for `Nat`
data SNat n where
SZero :: SNat Z
SSucc :: SNat n -> SNat (S n)
-- Utility for taking the predecessor of an `SNat`
sub1 :: SNat (S n) -> SNat n
sub1 (SSucc x) = x
-- Find the size of a list
size :: List a n -> SNat n
size Nil = SZero
size (Cons _ xs) = SSucc (size xs)
Теперь мы в форме, чтобы начать доказывать некоторые вещи. Из Data.Type.Equality
, a :~: b
представляет доказательство того, что a ~ b
. Нам нужно доказать одну простую вещь об арифметике.
-- Proof that n + (S m) == S (n + m)
plusSucc :: SNat n -> SNat m -> (n + S m) :~: S (n + m)
plusSucc SZero _ = Refl
plusSucc (SSucc n) m = gcastWith (plusSucc n m) Refl
Наконец, мы можем использовать gcastWith
, чтобы использовать это доказательство в aux
. О, и вам не удалось ограничить Ord a
.:)
aux :: Ord a => List a n -> a -> List a m -> List a (n + S m)
aux Nil prev acc = Cons prev acc
aux (Cons hd tl) prev acc = gcastWith (plusSucc (size tl) (SSucc (size acc)))
aux tl hd (Cons (max hd prev) acc)
-- append to a list
(|>) :: List a n -> a -> List a (S n)
Nil |> y = Cons y Nil
(Cons x xs) |> y = Cons x (xs |> y)
-- reverse 'List'
rev :: List a n -> List a n
rev Nil = Nil
rev (Cons x xs) = rev xs |> x
Сообщите мне, если это ответит на ваш вопрос - начало работы с такими вещами связано с большим количеством новых вещей.