我无法确定如何在类型类的规范中表达操作必须可交换的契约。
举个简单例子:
class Direction a where
turnLeft :: a -> a
turnRight :: a -> a
不能保证turnLeft . turnRight
等同于turnRight . turnLeft
。我想备选方案是指定单子律的等效方式 - 使用注释来指定类型系统未强制执行的约束条件。
class Direction a where
turnLeft :: a -> a
turnRight :: a -> a
不能保证turnLeft . turnRight
等同于turnRight . turnLeft
。我想备选方案是指定单子律的等效方式 - 使用注释来指定类型系统未强制执行的约束条件。
class Direction a where
turnLeft :: a -> a
turnRight :: a -> a
proofburden (forall a. turnLeft (turnRight a) === turnRight (turnLeft a))
所有实例必须提供函数和证明以供编译器进行类型检查。这对于Haskell来说是一种美好的愿望,因为Haskell没有(或者只有有限的)证明概念。
另一方面,Coq是一种依赖类型语言的证明助手,可以提取到Haskell中。虽然我从未使用过Coq 的类型类,但通过快速搜索可以找到如下示例:
Class EqDec (A : Type) := {
eqb : A -> A -> bool ;
eqb_leibniz : forall x y, eqb x y = true -> x = y }.
看起来高级语言可以做到这一点,但要让普通开发人员更容易上手仍有相当多的工作要做。
回答TomMD的问题后,您可以使用Agda来达到相同的效果。虽然它没有类型类,但您可以从记录中获得大部分功能(除了动态调度)。
record Direction (a : Set) : Set₁ where
field
turnLeft : a → a
turnRight : a → a
commLaw : ∀ x → turnLeft (turnRight x) ≡ turnRight (turnLeft x)
我想编辑这篇文章并回答为什么你不能在Haskell中这样做的问题。
在Haskell(+扩展)中,你可以像上面Agda代码中所使用的那样表示等价性。
{-# LANGUAGE GADTs, KindSignatures, TypeOperators #-}
data (:=:) a :: * -> * where
Refl :: a :=: a
这代表了两种类型相等的定理。例如,a
等价于b
是a :=: b
。
当它们是等价的时候,我们可以使用构造器Refl
。使用它,我们可以在定理(类型)的证明(值)上执行函数。
-- symmetry
sym :: a :=: b -> b :=: a
sym Refl = Refl
-- transitivity
trans :: a :=: b -> b :=: c -> a :=: c
trans Refl Refl = Refl
wrong :: a :=: b
wrong = Refl
明显是错误的,确实无法通过类型检查。
然而,在所有这些过程中,值和类型之间的障碍并没有被打破。值、值级函数和证明仍然存在于冒号的一侧;类型、类型级函数和定理存在于另一侧。你的turnLeft
和turnRight
是值级函数,因此不能参与定理。
Agda和Coq是依赖类型语言,其中障碍不存在或允许跨越。Strathclyde Haskell Enhancement (SHE)是Haskell代码的预处理器,可以欺骗DTP的一些效果进入Haskell。它通过在类型世界中复制来自值世界的数据来实现这一点。我认为它还没有处理复制值级函数的能力,如果它这样做了,我的直觉是这可能对它来说太复杂了。
我无法确定如何在类型类的规范中表达操作必须交换的合同条款。
你无法确定的原因是这是不可能的。至少在Haskell中,你无法在类型中编码这种属性。