我试图用(偶数/奇数)奇偶性类型来标记规范的Nat数据类型,看看我们是否可以得到任何自由定理。这是代码:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE DataKinds #-}
-- Use DataKind promotion with type function for even-odd
module EvenOdd where
data Parity = Even | Odd
-- Parity is promoted to kind level Parity.
-- Even & Odd to type level 'Even & 'Odd of kind Parity
-- We define type-function opp to establish the relation that
-- type 'Even is opposite of 'Odd, and vice-versa
type family Opp (n :: Parity) :: Parity
type instance Opp 'Even = 'Odd
type instance Opp 'Odd = 'Even
-- We tag natural number with the type of its parity
data Nat :: Parity -> * where
Zero :: Nat 'Even
Succ :: Nat p -> Nat (Opp p)
-- Now we (should) get free theorems.
-- 1. Plus of two even numbers is even
evenPlus :: Nat 'Even -> Nat 'Even -> Nat 'Even
evenPlus Zero n2 = n2 -- Line 31
evenPlus (Succ (Succ n1)) n2 = Succ (Succ (evenPlus n1 n2))
但是,GHC会抛出类型错误:
Could not deduce (p1 ~ 'Even)
from the context ('Even ~ Opp p)
bound by a pattern with constructor
Succ :: forall (p :: Parity). Nat p -> Nat (Opp p),
in an equation for `evenPlus'
at even-odd.hs:31:13-26
or from (p ~ Opp p1)
bound by a pattern with constructor
Succ :: forall (p :: Parity). Nat p -> Nat (Opp p),
in an equation for `evenPlus'
at even-odd.hs:31:19-25
`p1' is a rigid type variable bound by
a pattern with constructor
Succ :: forall (p :: Parity). Nat p -> Nat (Opp p),
in an equation for `evenPlus'
at even-odd.hs:31:19
Expected type: Nat 'Even
Actual type: Nat p
In the first argument of `evenPlus', namely `n1'
In the first argument of `Succ', namely `(evenPlus n1 n2)'
据我了解,上述错误的要点是当上下文具有等式时,GHC无法推导出(p1~'No):((Opp(Opp p1))〜'偶数)。
为什么会这样?我的做法有问题吗?
答案 0 :(得分:7)
我不认为GADT模式匹配细化是这样的。您有Opp p
作为构造函数的结果类型。所以,如果你写的东西像
f :: Nat 'Even -> ...
f (Succ n) = ...
然后类型检查器知道Nat (Opp t) ~ Nat 'Even
,因此知道Opp t ~ 'Even
。但要解决这个问题,类型检查器必须反转函数Opp
,这需要很多。
我建议您将Nat
的定义更改为:
data Nat :: Parity -> * where
Zero :: Nat 'Even
Succ :: Nat (Opp p) -> Nat p
这应该可行。
实际上,让我稍微扩展一下。
上述建议并非没有(次要)价格。你失去了一些类型推断。例如,Succ Zero
的类型现在是Succ Zero :: Opp p ~ 'Even => Nat p
而不是Nat 'Odd
。使用显式类型注释,它可以解决问题。
您可以通过向Succ
添加一个要求Opp
为自相反的约束来改进此问题。 Parity
的唯一两个元素是Even
和Odd
,对于这些元素,约束成立,所以它永远不会导致任何问题:
data Nat :: Parity -> * where
Zero :: Nat 'Even
Succ :: (Opp (Opp p) ~ p) => Nat (Opp p) -> Nat p
现在Succ Zero
被推断为Nat 'Odd
类型,模式匹配仍然有效。