Skip to content

Commit 68bf434

Browse files
committed
Add : declarations for val definitions
1 parent f5a080e commit 68bf434

File tree

2 files changed

+113
-34
lines changed

2 files changed

+113
-34
lines changed

hackett-lib/hackett/private/base.rkt

Lines changed: 111 additions & 33 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
@@ -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,54 @@
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 behaves differently in an expression context vs. a
380+
;; module context by dispatching on (syntax-local-context).
381+
(define-syntax :
382+
(λ (stx)
383+
(match (syntax-local-context)
384+
; In an expression context, `:` annotates an expression with
385+
; an expected type.
386+
['expression
387+
(syntax-parse stx
388+
; The #:exact option prevents : from performing context reduction. This is not normally important,
389+
; but it is required for forms that use : to ensure an expression has a particular shape in order to
390+
; interface with untyped (i.e. Racket) code, and therefore the resulting type is ignored. For
391+
; example, the def form wraps an expression in its expansion with :, but it binds the actual
392+
; identifier to a syntax transformer that attaches the type directly. Therefore, it needs to perform
393+
; context reduction itself prior to expanding to :, and it must use #:exact.
394+
[(_ e {~type t:type} {~optional {~and #:exact exact?}})
395+
#:with t_reduced (if (attribute exact?)
396+
#'t.expansion
397+
(type-reduce-context #'t.expansion))
398+
(attach-type #`(let-values- ([() t.residual])
399+
#,(τ⇐! ((attribute t.scoped-binding-introducer) #'e) #'t_reduced))
400+
#'t_reduced)])]
401+
402+
; In other contexts, such as module-level bindings or
403+
; in the REPL, : is a type declaration for `x`, and
404+
; will be understood by `def`.
405+
[_
406+
(syntax-parse stx
407+
[(_ x:id {~type t:type}
408+
{~alt {~optional {~and #:exact exact?}}
409+
{~optional f:fixity-annotation}}
410+
...)
411+
#:with x- (generate-temporary #'x)
412+
#:with exct? (and (attribute exact?) #true)
413+
#:with fixity (attribute f.fixity)
414+
#:with t_reduced (if (attribute exact?)
415+
#'t.expansion
416+
(type-reduce-context #'t.expansion))
417+
#:with sbi (serialize-syntax-introducer
418+
(attribute t.scoped-binding-introducer))
419+
#`(begin-
420+
(define-values- [] t.residual)
421+
(define-syntax- x
422+
(binding-declaration
423+
(quote-syntax x-)
424+
(quote-syntax t_reduced)
425+
(quote-syntax sbi)
426+
'fixity)))])])))
360427

361428
(define-syntax-parser λ1
362429
[(_ x:id e:expr)
@@ -383,31 +450,42 @@
383450

384451
(define-syntax-parser def
385452
#: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-
...
453+
[(_ x:id/binding-declaration e:expr)
454+
#:with x- #'x.internal-id
455+
(syntax-property
456+
#`(define- x-
457+
(: #,((attribute x.scoped-binding-introducer) #'e)
458+
x.type
459+
#:exact))
460+
'disappeared-use
461+
(syntax-local-introduce #'x))]
462+
463+
[(_ x:id {~and : {~var :/use}} type:expr
464+
{~and {~seq stuff ...}
465+
{~seq {~alt {~optional {~and #:exact exact?}}
466+
{~optional f:fixity-annotation}}
467+
...}}
391468
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)))]
469+
#'(begin-
470+
(:/use x type stuff ...)
471+
(def x e))]
472+
399473
[(_ id:id
400-
{~optional fixity:fixity-annotation}
474+
{~and {~seq fixity-stuff ...}
475+
{~optional fixity:fixity-annotation}}
401476
e:expr)
402-
#:with x^ (generate-temporary)
477+
#:with x^ (generate-temporary #'z)
403478
#:with t_e #'(#%type:wobbly-var x^)
404479
#:do [(match-define-values [(list id-) e-] (τ⇐/λ! #'e #'t_e (list (cons #'id #'t_e))))]
405480
#:with t_gen (type-reduce-context (generalize (apply-current-subst #'t_e)))
481+
#:with id-/gen (attach-type id- #'t_gen)
406482
#`(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-))])
483+
(: id t_gen fixity-stuff ... #:exact)
484+
(define/binding-declaration id
485+
(let-syntax ([id-/gen
486+
(make-rename-transformer (quote-syntax id))])
487+
#,e-)))])
488+
411489

412490
(begin-for-syntax
413491
(struct todo-item (full summary) #:prefab))

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)