SML 데이터 유형의 하위 집합 인 하위 유형


10

순전히 기능적인 데이터 구조에 대한 오카 사키의 책에 대해 내가 싫어하는 몇 가지 중 하나는 코드가 완전한 패턴 일치로 흩어져 있다는 것입니다. 예를 들어, 실시간 대기열 구현 (불필요한 중단을 제거하기 위해 리팩토링)을 제공합니다.

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 유형의 유도 적으로 정의 된 부분 집합을 "조각"하여 하위 유형을 정의하려고합니다. 이것이 가능합니까?

답변:


20

허용 가능한 값의 문법을 제공하여 하위 유형 (기본적으로)을 정의하는 이러한 유형의 유형을 데이터 정렬 개선 이라고 합니다.

  • 이들은 Tim Freeman과 Frank Pfenning에 의해 1991 년 PLDI 논문 인 ML을위한 정제 유형에 소개되었습니다 .

  • Rowan Davies는 박사 학위 논문 인 Practical Refinement Type Checking 에서 정제 유형에 대한 유형 유추를 연구했습니다 . 그는 또한 SML의 확장으로 구현했지만 온라인에서 사용할 수 있는지 모르겠습니다.

  • Joshua Dunfield는 자신의 논문에서 유형 분류 의 통합 시스템 이라는 논문에서 데이터 정렬 개선을 더 가벼운 경량 유형 종속성과 결합하는 방법을 연구했습니다 . 그는 온라인에서 사용할 수있는 Stardust 언어로 도 구현했습니다 .

    http://www.mpi-sws.org/~joshua/stardust/


3
Rowan Davies의 구현은 여기에서 볼 수 있습니다 : github.com/rowandavies/sml-cidre
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을 제공하기 때문에, 또한 GADTs 너무 불편 해요 Foo a, 당신은 두 개의 동형 유형을 가질 수 Bar하고 Qux, 그러한 Foo BarFoo Qux동형 수 없습니다. 함수 맵이 같음이라는 수학적 직관과 모순됩니다. 유형 수준에서 동형은 올바른 평등 개념입니다.
pyon

나는 당신의 자질을 이해하지만 실제로 일반화 된 특수한 일반화를 허용합니다.
사무엘 슐레진저
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.