Skip to content

Commit 69fc981

Browse files
committed
create example linear language + tests
1 parent a46c66a commit 69fc981

File tree

2 files changed

+253
-0
lines changed

2 files changed

+253
-0
lines changed

turnstile/examples/linear.rkt

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
#lang turnstile
2+
3+
(provide (type-out Unit Int Str Bool -o ⊗ !!)
4+
#%top-interaction #%module-begin require only-in
5+
#%datum begin tup let let+ λ #%app if
6+
(rename-out [λ lambda]))
7+
8+
(provide (typed-out [+ : (!! (-o Int Int Int))]
9+
[< : (!! (-o Int Int Bool))]
10+
[displayln : (!! (-o Str Unit))]))
11+
12+
(define-base-types Unit Int Str Bool)
13+
(define-type-constructor -o #:arity >= 1)
14+
(define-type-constructor ⊗ #:arity = 2)
15+
(define-type-constructor !! #:arity = 1)
16+
17+
(begin-for-syntax
18+
(require syntax/id-set)
19+
(define (sym-diff s0 . ss)
20+
(for*/fold ([s0 s0])
21+
([s (in-list ss)]
22+
[x (in-set s)])
23+
(if (set-member? s0 x)
24+
(set-remove s0 x)
25+
(set-add s0 x))))
26+
27+
28+
(define unrestricted-type?
29+
(or/c Int? Str? !!?))
30+
31+
32+
(define used-vars (immutable-free-id-set))
33+
34+
(define (lin-var-in-scope? x)
35+
(not (set-member? used-vars x)))
36+
37+
(define (use-lin-var x)
38+
(unless (lin-var-in-scope? x)
39+
(raise-syntax-error #f "linear variable used more than once" x))
40+
(set! used-vars (set-add used-vars x)))
41+
42+
(define (pop-vars xs ts)
43+
(set! used-vars
44+
(for/fold ([uv used-vars])
45+
([x (in-syntax xs)]
46+
[t (in-syntax ts)])
47+
(unless (unrestricted-type? t)
48+
(when (lin-var-in-scope? x)
49+
(raise-syntax-error #f "linear variable unused" x)))
50+
(set-remove uv x))))
51+
52+
53+
54+
(define scope-stack '())
55+
56+
(define (save-scope)
57+
(set! scope-stack (cons used-vars scope-stack)))
58+
59+
(define (merge-scope #:fail-msg fail-msg
60+
#:fail-src [fail-src #f])
61+
(for ([x (in-set (sym-diff used-vars (car scope-stack)))])
62+
(raise-syntax-error #f fail-msg fail-src x))
63+
(set! scope-stack (cdr scope-stack)))
64+
65+
(define (swap-scope)
66+
(set! used-vars
67+
(begin0 (car scope-stack)
68+
(set! scope-stack
69+
(cons used-vars (cdr scope-stack))))))
70+
71+
)
72+
73+
74+
(define-typed-syntax #%top-interaction
75+
[(_ . e) ≫
76+
[⊢ e ≫ e- ⇒ τ]
77+
--------
78+
[≻ (#%app- printf- '"~a : ~a\n"
79+
e-
80+
'#,(type->str #'τ))]])
81+
82+
83+
(define-typed-variable-syntax (LIN x- : σ)
84+
[x ≫
85+
#:when (unrestricted-type? #'σ)
86+
--------
87+
[⊢ x- ⇒ σ]]
88+
[x ≫
89+
#:do [(use-lin-var #'x-)]
90+
--------
91+
[⊢ x- ⇒ σ]])
92+
93+
94+
(define-typed-syntax #%datum
95+
[(_ . n:exact-integer) ≫
96+
--------
97+
[⊢ (#%datum- . n) ⇒ Int]]
98+
[(_ . b:boolean) ≫
99+
--------
100+
[⊢ (#%datum- . b) ⇒ Bool]]
101+
[(_ . s:str) ≫
102+
--------
103+
[⊢ (#%datum- . s) ⇒ Str]]
104+
[(_ . x) ≫
105+
--------
106+
[#:error (type-error #:src #'x #:msg "Unsupported literal: ~v" #'x)]])
107+
108+
109+
(define-typed-syntax begin
110+
[(_ e ... e0) ≫
111+
[⊢ [e ≫ e- ⇒ _] ... [e0 ≫ e0- ⇒ σ]]
112+
--------
113+
[⊢ (begin- e- ... e0-) ⇒ σ]])
114+
115+
116+
(define-typed-syntax tup
117+
#:datum-literals (!)
118+
[(_ e1 e2) ≫
119+
[⊢ e1 ≫ e1- ⇒ σ1]
120+
[⊢ e2 ≫ e2- ⇒ σ2]
121+
--------
122+
[⊢ (#%app- list- e1- e2-) ⇒ (⊗ σ1 σ2)]]
123+
124+
[(_ ! e1 e2) ≫
125+
#:do [(save-scope)]
126+
[⊢ e1 ≫ e1- ⇒ σ1]
127+
[⊢ e2 ≫ e2- ⇒ σ2]
128+
#:do [(merge-scope #:fail-msg "linear variable may not be used by unrestricted tuple"
129+
#:fail-src this-syntax)]
130+
--------
131+
[⊢ (#%app- list- e1- e2-) ⇒ (!! (⊗ σ1 σ2))]])
132+
133+
134+
(define-typed-syntax let
135+
[(let ([x rhs] ...) e) ≫
136+
[⊢ [rhs ≫ rhs- ⇒ σ] ...]
137+
[[LIN x ≫ x- : σ] ... ⊢ e ≫ e- ⇒ σ_out]
138+
#:do [(pop-vars #'(x- ...) #'(σ ...))]
139+
--------
140+
[⊢ (let- ([x- rhs-] ...) e-) ⇒ σ_out]])
141+
142+
143+
(define-typed-syntax λ
144+
#:datum-literals (: !)
145+
; linear function
146+
[(λ ([x:id : ty:type] ...) e) ≫
147+
#:with...) #'(ty.norm ...)
148+
[[LIN x ≫ x- : σ] ... ⊢ e ≫ e- ⇒ σ_out]
149+
#:do [(pop-vars #'(x- ...) #'(σ ...))]
150+
--------
151+
[⊢ (λ- (x- ...) e-) ⇒ (-o σ ... σ_out)]]
152+
153+
; unrestricted function
154+
[(λ ! ([x:id : ty:type] ...) e) ≫
155+
#:do [(save-scope)]
156+
#:with...) #'(ty.norm ...)
157+
[[LIN x ≫ x- : σ] ... ⊢ e ≫ e- ⇒ σ_out]
158+
#:do [(pop-vars #'(x- ...) #'(σ ...))
159+
(merge-scope #:fail-msg "linear variable may not be used by unrestricted function"
160+
#:fail-src this-syntax)]
161+
--------
162+
[⊢ (λ- (x- ...) e-) ⇒ (!! (-o σ ... σ_out))]])
163+
164+
165+
(define-typed-syntax #%app
166+
[(_) ≫
167+
--------
168+
[⊢ (#%app- void-) ⇒ Unit]]
169+
170+
[(#%app fun arg ...) ≫
171+
[⊢ fun ≫ fun- ⇒ σ_fun]
172+
#:with (~or (~-o σ_in ... σ_out)
173+
(~!! (~-o σ_in ... σ_out))
174+
(~post (~fail "expected function type")))
175+
#'σ_fun
176+
[⊢ [arg ≫ arg- ⇐ σ_in] ...]
177+
--------
178+
[⊢ (#%app- fun- arg- ...) ⇒ σ_out]])
179+
180+
181+
(define-typed-syntax if
182+
[(if c e1 e2) ≫
183+
[⊢ c ≫ c- ⇐ Bool]
184+
#:do [(save-scope)]
185+
[⊢ e1 ≫ e1- ⇒ σ]
186+
#:do [(swap-scope)]
187+
[⊢ e2 ≫ e2- ⇐ σ]
188+
#:do [(merge-scope #:fail-msg "linear variable may be unused in certain branches"
189+
#:fail-src this-syntax)]
190+
--------
191+
[⊢ (if- c- e1- e2-) ⇒ σ]])
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#lang s-exp "../linear.rkt"
2+
(require "rackunit-typechecking.rkt")
3+
(require (only-in racket/base quote))
4+
5+
(check-type #t : Bool)
6+
(check-type 4 : Int)
7+
(check-type () : Unit)
8+
9+
(check-type (tup 1 #t) : (⊗ Int Bool) -> '(1 #t))
10+
(check-type (tup 1 (tup 2 3)) : (⊗ Int (⊗ Int Int)) -> '(1 (2 3)))
11+
12+
(check-type (let ([x 3] [y 4]) y) : Int -> 4)
13+
(check-type (let ([p (tup 1 2)]) p) : (⊗ Int Int) -> '(1 2))
14+
15+
(typecheck-fail (let ([p (tup 1 2)]) ())
16+
#:with-msg "p: linear variable unused")
17+
(typecheck-fail (let ([p (tup 1 2)]) (tup p p))
18+
#:with-msg "p: linear variable used more than once")
19+
20+
(check-type (if #t 1 2) : Int -> 1)
21+
(typecheck-fail (if 1 2 3)
22+
#:with-msg "expected Bool, given Int")
23+
(typecheck-fail (if #t 2 ())
24+
#:with-msg "expected Int, given Unit")
25+
26+
(check-type (let ([p (tup 1 ())]) (if #t p p)) : (⊗ Int Unit))
27+
(typecheck-fail (let ([p (tup 1 ())]) (if #t p (tup 2 ())))
28+
#:with-msg "linear variable may be unused in certain branches")
29+
(typecheck-fail (let ([p (tup 1 ())]) (if #t p (begin p p)))
30+
#:with-msg "p: linear variable used more than once")
31+
32+
33+
(check-type (λ ([x : Int]) (tup x x)) : (-o Int (⊗ Int Int)))
34+
(check-type (λ ([x : (⊗ Int Int)]) x) : (-o (⊗ Int Int) (⊗ Int Int)))
35+
(typecheck-fail (λ ([x : (⊗ Int Int)]) ())
36+
#:with-msg "x: linear variable unused")
37+
38+
(check-type (let ([p (tup 1 2)]) (λ ([x : Int]) p))
39+
: (-o Int (⊗ Int Int)))
40+
41+
(check-type (λ ! ([x : Int]) x) : (!! (-o Int Int)))
42+
(typecheck-fail (let ([p (tup 1 2)]) (λ ! ([x : Int]) p))
43+
#:with-msg "linear variable may not be used by unrestricted function\n at: p")
44+
45+
46+
(check-type (let ([f (λ ([x : Int] [y : Int]) y)])
47+
(f 3 4))
48+
: Int -> 4)
49+
(check-type + : (!! (-o Int Int Int)))
50+
(check-type (+ 1 2) : Int -> 3)
51+
(check-type (< 3 4) : Bool -> #t)
52+
53+
54+
(check-type (let ([×2 (λ ! ([x : Int]) (+ x x))])
55+
(+ (×2 8)
56+
(×2 9)))
57+
: Int -> 34)
58+
59+
(typecheck-fail (let ([×2 (λ ([x : Int]) (+ x x))])
60+
(+ (×2 8)
61+
(×2 9)))
62+
#:with-msg "×2: linear variable used more than once")

0 commit comments

Comments
 (0)