Skip to content

Commit 3b690c8

Browse files
committed
Add declarations for val definitions
1 parent f5a080e commit 3b690c8

File tree

3 files changed

+114
-36
lines changed

3 files changed

+114
-36
lines changed

hackett-lib/hackett/private/base.rkt

Lines changed: 111 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
syntax/parse/experimental/template
77
syntax/intdef
88
syntax/srcloc
9-
threading)
9+
syntax/parse/class/local-value
10+
threading
11+
serialize-syntax-introducer)
1012
(postfix-in - (combine-in racket/base
1113
racket/promise))
1214
racket/stxparam
@@ -26,7 +28,7 @@
2628
@%superclasses-key @%dictionary-placeholder @%with-dictionary #%defer-expansion
2729
define-primop define-base-type
2830
-> Integer Double String
29-
: λ1 def let letrec todo!)
31+
: λ1 def let letrec todo!)
3032

3133
(define-base-type Integer)
3234
(define-base-type Double)
@@ -324,6 +326,37 @@
324326

325327
;; ---------------------------------------------------------------------------------------------------
326328

329+
(begin-for-syntax
330+
; Instances of this struct are created by `⋮` when declaring the types of bindings
331+
; seperately from their definitions. When a binding is defined (with `def` or related
332+
; forms), it searches for a `binding-declaration` and fills in `internal-id` with the
333+
; actual definition. The `type` field is used as the expected type of the definition.
334+
; fixity : [Maybe Fixity]
335+
(struct binding-declaration [internal-id type scoped-binding-introducer fixity]
336+
#:property prop:procedure
337+
(λ (this stx)
338+
(match-define (binding-declaration x- type _ _) this)
339+
((make-typed-var-transformer x- type) stx))
340+
#:property prop:infix-operator
341+
(λ (this) (binding-declaration-fixity this)))
342+
343+
(define-syntax-class id/binding-declaration
344+
#:attributes [internal-id type scoped-binding-introducer fixity]
345+
[pattern (~var x (local-value binding-declaration?))
346+
#:do [(match-define (binding-declaration x-* type* sbi* fixity*)
347+
(attribute x.local-value))]
348+
#:attr internal-id (syntax-local-introduce x-*)
349+
#:with type (syntax-local-introduce type*)
350+
#:attr scoped-binding-introducer (deserialize-syntax-introducer sbi*)
351+
#:attr fixity fixity*]))
352+
353+
(define-syntax-parser define/binding-declaration
354+
[(_ x:id/binding-declaration rhs)
355+
#:with x- #'x.internal-id
356+
#'(define- x- rhs)])
357+
358+
;; ---------------------------------------------------------------------------------------------------
359+
327360
(define-syntax-parser @%module-begin
328361
[(_ form ...)
329362
(~> (local-expand (value-namespace-introduce
@@ -343,20 +376,53 @@
343376
[(_ . x)
344377
(raise-syntax-error #f "literal not supported" #'x)])
345378

346-
(define-syntax-parser :
347-
; The #:exact option prevents : from performing context reduction. This is not normally important,
348-
; but it is required for forms that use : to ensure an expression has a particular shape in order to
349-
; interface with untyped (i.e. Racket) code, and therefore the resulting type is ignored. For
350-
; example, the def form wraps an expression in its expansion with :, but it binds the actual
351-
; identifier to a syntax transformer that attaches the type directly. Therefore, it needs to perform
352-
; context reduction itself prior to expanding to :, and it must use #:exact.
353-
[(_ e {~type t:type} {~optional {~and #:exact exact?}})
354-
#:with t_reduced (if (attribute exact?)
355-
#'t.expansion
356-
(type-reduce-context #'t.expansion))
357-
(attach-type #`(let-values- ([() t.residual])
358-
#,(τ⇐! ((attribute t.scoped-binding-introducer) #'e) #'t_reduced))
359-
#'t_reduced)])
379+
;; The `:` form annotates an expression with an expected type.
380+
(define-syntax :
381+
(λ (stx)
382+
(syntax-parse stx
383+
; The #:exact option prevents : from performing context reduction. This is not normally important,
384+
; but it is required for forms that use : to ensure an expression has a particular shape in order to
385+
; interface with untyped (i.e. Racket) code, and therefore the resulting type is ignored. For
386+
; example, the def form wraps an expression in its expansion with :, but it binds the actual
387+
; identifier to a syntax transformer that attaches the type directly. Therefore, it needs to perform
388+
; context reduction itself prior to expanding to :, and it must use #:exact.
389+
[(_ e {~type t:type} {~optional {~and #:exact exact?}})
390+
#:with t_reduced (if (attribute exact?)
391+
#'t.expansion
392+
(type-reduce-context #'t.expansion))
393+
(attach-type #`(let-values- ([() t.residual])
394+
#,(τ⇐! ((attribute t.scoped-binding-introducer) #'e) #'t_reduced))
395+
#'t_reduced)])))
396+
397+
;; The `⋮` form declares that an id will be defined with an expected type.
398+
;; For example:
399+
;; (⋮ x τ)
400+
;; (def x e)
401+
;; Defines `x` with type `τ` to be equal to the value produced by `e`,
402+
;; which must produce
403+
(define-syntax
404+
(λ (stx)
405+
(syntax-parse stx
406+
[(_ x:id {~type t:type}
407+
{~alt {~optional {~and #:exact exact?}}
408+
{~optional f:fixity-annotation}}
409+
...)
410+
#:with x- (generate-temporary #'x)
411+
#:with exct? (and (attribute exact?) #true)
412+
#:with fixity (attribute f.fixity)
413+
#:with t_reduced (if (attribute exact?)
414+
#'t.expansion
415+
(type-reduce-context #'t.expansion))
416+
#:with sbi (serialize-syntax-introducer
417+
(attribute t.scoped-binding-introducer))
418+
#`(begin-
419+
(define-values- [] t.residual)
420+
(define-syntax- x
421+
(binding-declaration
422+
(quote-syntax x-)
423+
(quote-syntax t_reduced)
424+
(quote-syntax sbi)
425+
'fixity)))])))
360426

361427
(define-syntax-parser λ1
362428
[(_ x:id e:expr)
@@ -383,31 +449,42 @@
383449

384450
(define-syntax-parser def
385451
#:literals [:]
386-
[(_ id:id
387-
{~or {~once {~seq {~and : {~var :/use}} {~type t:type}
388-
{~optional {~and #:exact exact?}}}}
389-
{~optional fixity:fixity-annotation}}
390-
...
452+
[(_ x:id/binding-declaration e:expr)
453+
#:with x- #'x.internal-id
454+
(syntax-property
455+
#`(define- x-
456+
(: #,((attribute x.scoped-binding-introducer) #'e)
457+
x.type
458+
#:exact))
459+
'disappeared-use
460+
(syntax-local-introduce #'x))]
461+
462+
[(_ x:id : type:expr
463+
{~and {~seq stuff ...}
464+
{~seq {~alt {~optional {~and #:exact exact?}}
465+
{~optional f:fixity-annotation}}
466+
...}}
391467
e:expr)
392-
#:with id- (generate-temporary #'id)
393-
#:with t_reduced (if (attribute exact?) #'t.expansion (type-reduce-context #'t.expansion))
394-
#`(begin-
395-
#,(indirect-infix-definition
396-
#'(define-syntax- id (make-typed-var-transformer #'id- (quote-syntax t_reduced)))
397-
(attribute fixity.fixity))
398-
(define- id- (:/use #,((attribute t.scoped-binding-introducer) #'e) t_reduced #:exact)))]
468+
#'(begin-
469+
(⋮ x type stuff ...)
470+
(def x e))]
471+
399472
[(_ id:id
400-
{~optional fixity:fixity-annotation}
473+
{~and {~seq fixity-stuff ...}
474+
{~optional fixity:fixity-annotation}}
401475
e:expr)
402-
#:with x^ (generate-temporary)
476+
#:with x^ (generate-temporary #'z)
403477
#:with t_e #'(#%type:wobbly-var x^)
404478
#:do [(match-define-values [(list id-) e-] (τ⇐/λ! #'e #'t_e (list (cons #'id #'t_e))))]
405479
#:with t_gen (type-reduce-context (generalize (apply-current-subst #'t_e)))
480+
#:with id-/gen (attach-type id- #'t_gen)
406481
#`(begin-
407-
#,(indirect-infix-definition
408-
#`(define-syntax- id (make-typed-var-transformer (quote-syntax #,id-) (quote-syntax t_gen)))
409-
(attribute fixity.fixity))
410-
(define- #,id- #,e-))])
482+
(⋮ id t_gen fixity-stuff ... #:exact)
483+
(define/binding-declaration id
484+
(let-syntax ([id-/gen
485+
(make-rename-transformer (quote-syntax id))])
486+
#,e-)))])
487+
411488

412489
(begin-for-syntax
413490
(struct todo-item (full summary) #:prefab))

hackett-lib/hackett/private/kernel.rkt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
[λ lambda])
2121
#%require/only-types combine-in except-in only-in prefix-in rename-in
2222
provide combine-out except-out prefix-out rename-out for-type module+
23-
: def λ let letrec todo!
23+
: def λ let letrec todo!
2424
(for-type #:no-introduce-> => Integer Double String
2525
(rename-out [@%top #%top]
2626
[#%type:app #%app]

hackett-lib/info.rkt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"curly-fn-lib"
88
"data-lib"
99
"syntax-classes-lib"
10-
"threading-lib"))
10+
"threading-lib"
11+
"serialize-syntax-introducer"))
1112
(define build-deps
1213
'())

0 commit comments

Comments
 (0)