Используя GHC.TypeLits
, мы можем написать простой нумерованный список типа (или вектор).
> {-# LANGUAGE TypeOperators, KindSignatures, GADTs, DataKinds, ScopedTypeVariables #-}
> import GHC.TypeLits
> data Vec :: * -> Nat -> * where
> VNil :: Vec e 0
> (:-) :: e -> Vec e n -> Vec e (n+1)
Это каноническое векторное определение с TypeLits
. Операция append, интуитивно, должна выглядеть следующим образом:
> vecAppend :: Vec e n -> Vec e m -> Vec e (n + m)
> vecAppend VNil vec = vec
> vecAppend (a :- as) vec = a :- vecAppend as vec
Но решатель GHC имеет проблемы с арифметикой:
Could not deduce (((n1 + m) + 1) ~ (n + m))
from the context (n ~ (n1 + 1))
Конечно, поскольку n1 + 1 ~ n
, (n1 + m) + 1 ~ n1 + 1 + m ~ n + m
, но решатель, похоже, не знает о коммутативности и ассоциативности +
(функция типа вообще не коммутативна или ассоциативна!)
Я знаю, что это возможно, если я определяю тип Peano naturals типа уровня, но я хотел знать, есть ли способ сделать это с текущей реализацией типа в GHC (7.8.0 здесь.)
Поэтому я попытался помочь:
> vecAppend :: Vec e (n+1) -> Vec e m -> Vec e ((n + 1) + m)
> vecAppend VNil vec = vec
> vecAppend (a :- as) vec = a :- vecAppend as vec
Но это просто отвлекает проблему от правильного экземпляра переменных типа.
Could not deduce (((n1 + 1) + m) ~ ((n + m) + 1))
from the context ((n + 1) ~ (n1 + 1))
bound by a pattern with constructor
:- :: forall e (n :: Nat). e -> Vec e n -> Vec e (n + 1),
in an equation for ‘vecAppend’
NB: ‘+’ is a type function, and may not be injective
Expected type: Vec l ((n + 1) + m)
Actual type: Vec l ((n + m) + 1)
Relevant bindings include
l :: Vec l m
as :: Vec l n1
vecAppend :: Vec l (n + 1) -> Vec l m -> Vec l ((n + 1) + m)
И еще два таких, как этот.
Итак, дайте им возможность.
> vecAppend ∷ ∀ e n m. Vec e (n+1) → Vec e m → Vec e (n + 1 + m)
> vecAppend VNil l = l
> vecAppend ((a :- (as ∷ Vec e n)) ∷ Vec e (n+1)) (l ∷ Vec e m) = a :- (vecAppend as l ∷ Vec e (n+m))
Увы,
Could not deduce (n1 ~ n)
from the context ((n + 1) ~ (n1 + 1))
bound by a pattern with constructor
:- :: forall e (n :: Nat). e -> Vec e n -> Vec e (n + 1),
in an equation for ‘vecAppend’
‘n1’ is a rigid type variable bound by
a pattern with constructor
:- :: forall e (n :: Nat). e -> Vec e n -> Vec e (n + 1),
in an equation for ‘vecAppend’
‘n’ is a rigid type variable bound by
the type signature for
vecAppend :: Vec e (n + 1) -> Vec e m -> Vec e ((n + 1) + m)
Expected type: Vec e n1
Actual type: Vec e n
Relevant bindings include
vecAppend :: Vec e (n + 1) -> Vec e m -> Vec e ((n + 1) + m)
In the pattern: as :: Vec e n
In the pattern: a :- (as :: Vec e n)
In the pattern: (a :- (as :: Vec e n)) :: Vec e (n + 1)
Есть ли способ сделать это с помощью текущего решателя и не прибегать к определению ваших собственных пиано-натов? Я предпочитаю внешний вид 3
до Succ (Succ (Succ Zero)))
в моих сигнатурах типа.
РЕДАКТИРОВАТЬ. Поскольку кажется, что в настоящее время нет способа сделать это (до GHC 7.10), я перефразирую свой вопрос: может кто-нибудь показать мне, почему нет пути? Я, к сожалению, еще не посмотрел на решателей SMT, поэтому я не знаю, что в принципе возможно или нет.
Идея заключается в том, что я не очень разбираюсь в вычислениях на уровне уровня, и хочу научиться распознавать ситуации, когда я могу перефразировать мою проблему, чтобы она работала, и ситуации, когда я не могу (это настоящее (на данный момент) экземпляр последнего.)