子类型作为SML数据类型的子集


10

我对Okasaki关于纯功能数据结构的书不满意的几件事之一是,他的代码充满了不尽详尽的模式匹配。举例来说,我将为他提供实时队列的实现(重构为消除不必要的挂起):

infixr 5 :::

datatype 'a stream = Nil | ::: of 'a * 'a stream lazy

structure RealTimeQueue :> QUEUE =
struct
  (* front stream, rear list, schedule stream *)
  type 'a queue = 'a stream * 'a list * 'a stream

  (* the front stream is one element shorter than the rear list *)
  fun rotate (x ::: $xs, y :: ys, zs) = x ::: $rotate (xs, ys, y ::: $zs)
    | rotate (Nil, y :: nil, zs) = y ::: $zs

  fun exec (xs, ys, _ ::: $zs) = (xs, ys, zs)
    | exec args = let val xs = rotate args in (xs, nil, xs) end

  (* public operations *)
  val empty = (Nil, nil, Nil)
  fun snoc ((xs, ys, zs), y) = exec (xs, y :: ys, zs)
  fun uncons (x ::: $xs, ys, zs) = SOME (x, exec (xs, ys, zs))
    | uncons _ = NONE
end

可以看出rotate,这并不详尽,因为它没有涵盖后面列表为空的情况。大多数标准ML实现都会对此产生警告。我们知道,后列表不可能为空,因为rotate的前提是后列表比前流长一个元素。但是类型检查器不知道-也不可能知道,因为这一事实在ML的类型系统中是无法表达的。

现在,我抑制此警告的解决方案是以下不雅行为:

  fun rotate (x ::: $xs, y :: ys, zs) = x ::: $rotate (xs, ys, y ::: $zs)
    | rotate (_, ys, zs) = foldl (fn (x, xs) => x ::: $xs) zs ys

但是我真正想要的是一个可以理解并不是每个三元组都是的有效参数的类型系统rotate。我想让类型系统定义如下类型:

type 'a triplet = 'a stream * 'a list * 'a stream

subtype 'a queue of 'a triplet
  = (Nil, nil, Nil)
  | (xs, ys, zs) : 'a queue => (_ ::: $xs, _ :: ys, zs)
  | (xs, ys, zs) : 'a queue => (_ ::: $xs, ys, _ ::: $zs)

然后推断:

subtype 'a rotatable of 'a triplet
  = (xs, ys, _) : 'a rotatable => (_ ::: $xs, _ :: ys, _)
  | (Nil, y :: nil, _)

subtype 'a executable of 'a triplet
  = (xs, ys, zs) : 'a queue => (xs, ys, _ ::: $zs)
  | (xs, ys, Nil) : 'a rotatable => (xs, ys, Nil)

val rotate : 'a rotatable -> 'a stream
val exec : 'a executable -> 'a queue

但是,我不希望使用成熟的依赖类型,甚至GADT,也不希望某些程序员使用其他疯狂的东西。我只想通过“雕刻”现有ML类型的归纳定义子集来定义子类型。这可行吗?

Answers:


20

这些类型-您通过给出可接受值的语法来定义子类型(基本上),这些类型称为数据排序优化


3
Rowan Davies的实现可在以下位置
Noam Zeilberger

1

我可以使用GADT,TypeFamilies,DataKinds和TypeOperators(仅出于美观目的)来创建您想要的东西:

data Term0 varb lamb letb where
    Lam :: lamb -> Term0 varb lamb letb -> Term0 varb lamb letb
    Let :: letb -> Term0 varb lamb letb -> Term0 varb lamb letb -> Term0 varb lamb letb
    Var :: varb -> Term0 varb lamb letb
    App :: Term0 varb lamb letb -> Term0 varb lamb letb -> Term0 varb lamb letb

type Term b = Term0 b b b

data Terms = Lets | Lams | Vars

type family  t /// (ty :: Terms) where
    Term0 a b c /// Vars = Term0 Void b c
    Term0 a b c /// Lams = Term0 a Void c
    Term0 a b c /// Lets = Term0 a b Void

Now, I can write functions with more refined types:

unlet :: Term b -> Term b /// Lets

感谢您的回答。我TypeFamilies完全不赞成GHC的原则依据:它破坏了参数性并释放了定理。我对GADT也不太满意,因为在给定GADT的情况下Foo a,您可以有两种同构类型BarQux,而Foo Barand Foo Qux不是同构的。这与函数映射等于等于的数学直觉相矛盾-并且在类型级别,同构是相等的正确概念。
pyon

我了解您的条件,但可以进行专门的概括,在实践中我认为这很有价值。
塞缪尔·施莱辛格
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.