diff --git a/flexible-with2.hl.rkt b/flexible-with2.hl.rkt index 0fa36dc..4338111 100644 --- a/flexible-with2.hl.rkt +++ b/flexible-with2.hl.rkt @@ -118,18 +118,9 @@ also be collapsed. representation), ꩜racket[idx→tree] generates tree-shaped code, transforming each leaf with ꩜racket[_leaf-wrapper], each intermediate node with ꩜racket[_node-wrapper] and each leaf not present in the initial list with - ꩜racket[none-wrapper]. + ꩜racket[none-wrapper].} - The ꩜racket[idx→tree] is a two-step curried function, and the first step - returns a function with the following signature: - ꩜defproc[(r [#:depth depth exact-nonnegative-integer?] - [#:vec-bounds vec-start exact-nonnegative-integer?] - #| |# [vec-after-end exact-nonnegative-integer?] - [#:leaf-bounds first-leaf exact-nonnegative-integer?] - #| |# [after-last-leaf exact-nonnegative-integer?]) - any/c]{} -} ꩜chunk[<idx→tree> (define/contract (idx→tree #:none-wrapper none-wrapper @@ -141,31 +132,75 @@ also be collapsed. #:vec-bounds vec-start vec-after-end #:leaf-bounds first-leaf after-last-leaf) (cond - [(= vec-start vec-after-end) - (none-wrapper depth)] - [(= (+ first-leaf 1) after-last-leaf) - (leaf-wrapper vec-start (vector-ref vec vec-start))] - [else - (let* ([leftmost-right-leaf (/ (+ first-leaf after-last-leaf) 2)] - [vec-left-branch-start vec-start] - [vec-left-branch-after-end - (find-≥ leftmost-right-leaf vec vec-start vec-after-end)] - [vec-right-branch-start vec-left-branch-after-end] - [vec-right-branch-after-end vec-after-end]) - (node-wrapper - (r #:depth (sub1 depth) - #:vec-bounds vec-left-branch-start - #| |# vec-left-branch-after-end - #:leaf-bounds first-leaf - #| |# leftmost-right-leaf) - (r #:depth (sub1 depth) - #:vec-bounds vec-right-branch-start - #| |# vec-right-branch-after-end - #:leaf-bounds leftmost-right-leaf - #| |# after-last-leaf)))])) + |<idx→tree cases>|)) r)] -꩜chunk[<idx→tree/ctc> +The ꩜racket[idx→tree] is a two-step curried function, and the first step +returns a function with the following signature: + +꩜defproc[(r [#:depth depth exact-nonnegative-integer?] + [#:vec-bounds vec-start exact-nonnegative-integer?] + #;| | [vec-after-end exact-nonnegative-integer?] + [#:leaf-bounds first-leaf exact-nonnegative-integer?] + #;| | [after-last-leaf exact-nonnegative-integer?]) + any/c]{} + +The ꩜racket[idx→tree] function works by performing repeated dichotomy on the +vector of present fields ꩜racket[vec]. + +꩜hlite[|<idx→tree cases>| {-/ (cond = _ _ _)} + (cond + [(= vec-start vec-after-end) + (none-wrapper depth)] + [(= (+ first-leaf 1) after-last-leaf) + (leaf-wrapper vec-start (vector-ref vec vec-start))] + [else + (let* ([leftmost-right-leaf (/ (+ first-leaf after-last-leaf) 2)] + [vec-left-branch-start vec-start] + [vec-left-branch-after-end + (find-≥ leftmost-right-leaf vec vec-start vec-after-end)] + [vec-right-branch-start vec-left-branch-after-end] + [vec-right-branch-after-end vec-after-end]) + (node-wrapper + (r #:depth (sub1 depth) + #:vec-bounds vec-left-branch-start + #;| | vec-left-branch-after-end + #:leaf-bounds first-leaf + #;| | leftmost-right-leaf) + (r #:depth (sub1 depth) + #:vec-bounds vec-right-branch-start + #;| | vec-right-branch-after-end + #:leaf-bounds leftmost-right-leaf + #;| | after-last-leaf)))])] + +The ꩜racket[find-≥] function performs the actual dichotomy, and finds the +first index within the given bounds for which ꩜racket[(vector-ref vec result)] +is greater than or equal to ꩜racket[val] (if there is none, then the upper +exclusive bound is returned). + +꩜chunk[<dichotomy> + (define/contract (find-≥ val vec start end) + (->i ([val exact-nonnegative-integer?] + [vec vector?] ;; (sorted-vectorof exact-nonnegative-integer? <) + [start (vec) (integer-in 0 (sub1 (vector-length vec)))] + [end (vec start) (integer-in (add1 start) (vector-length vec))]) + #:pre (val vec start) (or (= start 0) + (< (vector-ref vec (sub1 start)) val)) + #:pre (val vec end) (or (= end (vector-length vec)) + (> (vector-ref vec end) val)) + [result (start end) (integer-in start end)]) + (if (= (- end start) 1) ;; there is only one element + (if (>= (vector-ref vec start) val) + start + end) + (let () + (define mid (ceiling (/ (+ start end) 2))) + (if (>= (vector-ref vec mid) val) + (find-≥ val vec start mid) + (find-≥ val vec mid end)))))] + +꩜;TODO: use a "spoiler" to hide the contents. +꩜chunk[#:save-as hidden:<idx→tree/ctc> <idx→tree/ctc> (-> #:none-wrapper (->d ([depth exact-nonnegative-integer?]) [result any/c]) #:leaf-wrapper (->d ([i-in-vec exact-nonnegative-integer?] @@ -177,14 +212,18 @@ also be collapsed. #:vec (vectorof exact-nonnegative-integer?) (-> #:depth exact-nonnegative-integer? #:vec-bounds exact-nonnegative-integer? - #| |# exact-nonnegative-integer? + #;| | exact-nonnegative-integer? #:leaf-bounds exact-nonnegative-integer? - #| |# exact-nonnegative-integer? + #;| | exact-nonnegative-integer? any/c))] -꩜section{Creating a record builder given a set of field indices} +꩜section{Empty branches (branches only containing missing fields)} -꩜chunk[<record-builder> +For efficiency and to reduce memory overhead, we pre-define values for +branches of depth ꩜racket[_d ∈ (range 0 _n)] which only contain missing +fields. + +꩜chunk[<empty-branches> ;; TODO: clean this up (use subtemplate). (define-syntax defempty (syntax-parser @@ -206,31 +245,16 @@ also be collapsed. … (provide empty1 emptyA … empty1/τ emptyA/τ …)))])) - (defempty 10) - - ;; The range is [start .. end[, that is start is part of the range, - ;; but end is not. - ;; Returns a value in the range start .. end (if end is returned, then - ;; it means that all elements in the range (which excludes end) are - ;; greater than val). - (define-for-syntax (find-≥ val vec start end) - ;; TODO: assert that vec[start] < val (actually: it's not the case) - ;; TODO: assert that vec[end] ≥ val - (define n-elements (- end start)) - (if (= n-elements 1) ;; there is only one element - (if (>= (vector-ref vec start) val) - start - end) - (let () - (define mid (ceiling (/ (+ start end) 2))) - (if (>= (vector-ref vec mid) val) - (find-≥ val vec start mid) - (find-≥ val vec mid end))))) + (defempty 10)] + +꩜section{Creating a record builder given a set of field indices} + +꩜chunk[<record-builder> (define-syntax record-builder (syntax-parser ;; depth ≥ 0. 0 ⇒ a single node, 1 ⇒ 2 nodes, 2 ⇒ 4 nodes and so on. [(_ depth:nat idx:nat …) - ;; TODO: check that the IDs are unique. + #:when (not (check-duplicates (syntax->datum #'(idx …)))) (define vec (list->vector (sort (syntax->datum #'(idx …)) <))) (define arg-vec (vector-map (λ (idx) (format-id #;TODO:FIXME: (syntax-local-introduce #'here) @@ -260,6 +284,110 @@ also be collapsed. ꩜section{Type of a record, with a multiple holes} +꩜; TODO: this is near identical to record-builder, refactor. +꩜chunk[<record-type> + (define-type-expander record-type + (syntax-parser + [(_ depth:nat idx:nat …) + #:when (not (check-duplicates (syntax->datum #'(idx …)))) + (define vec (list->vector (sort (syntax->datum #'(idx …)) <))) + (define arg-vec (vector-map (λ (idx) + (format-id #;TODO:FIXME: (syntax-local-introduce #'here) + "arg~a" + idx)) + vec)) + (define/with-syntax #(arg …) arg-vec) + (define sidekicks-len 0) + (define sidekicks '()) + (define/with-syntax tree + ((idx→tree <record-type/wrappers> + #:vec vec) + #:depth (syntax-e #'depth) + #:vec-bounds 0 (vector-length vec) + #:leaf-bounds 0 (expt 2 (syntax-e #'depth)))) + #`(∀ (arg … #,@sidekicks) tree)]))] + +꩜hlite[<record-type/wrappers> {-/ (_ = _ _ _ _ _ _ _ _ _ -/ _ vec)} + (idx→tree + #:none-wrapper λdepth.(begin + (define sidekick (format-id #'here "row~a" sidekicks-len)) + (set! sidekicks-len (add1 sidekicks-len)) + (set! sidekicks (cons sidekick sidekicks)) + sidekick) + #:leaf-wrapper λi.idx.(vector-ref arg-vec i) + #:node-wrapper λl.r.(list #'Pairof l r) + #:vec vec)] + +꩜chunk[<record-type> + (begin-for-syntax + (struct ρ-wrapper (id) + #:property prop:procedure + (λ (stx) + (raise-syntax-error + (if (and (stx-pair? stx) (identifier? (stx-car stx))) + (syntax-e (stx-car stx)) + 'ρ) + "invalid use of row type variable" + stx)))) + (define-syntax with-ρ + (syntax-parser + [(_ (ρ …) . body) + #'(splicing-letrec-syntax ([ρ (ρ-wrapper #'ρ)] + …) + . body)])) + + (define-for-syntax ρ-table (make-free-id-table)) + (define-type-expander ∀ρ + (syntax-parser + [(_ (A:id … #:ρ ρ:id …) τ) + (for ([ρ (in-syntax #'(ρ …))]) + (free-id-table-set! ρ-table ρ <make-lifo-set>)) + (define expanded (expand-type #'(∀ (A …) + (Let ([ρ (Pairof 'Hello 'ρ)] + …) + τ)))) + (define/syntax-parse ({~literal tr:∀} (A′ …) . τ′) expanded) + ;; TODO: do this in the delayed part, after the whole program has + ;; been expanded, so that we can know which fields ρ is used with. + (define/syntax-parse (ρ′ …) + (remove-duplicates + (append* (for/list ([ρ (in-syntax #'(ρ …))]) + (<lifo-set→list> (free-id-table-ref ρ-table ρ)))))) + (displayln #'(tr:∀ (A′ …) . τ′)) + (lift-delayed-module-end-declaration + #'(define-type delayed-type + (∀ (A′ … ρ′ …) . τ′))) + #'delayed-type])) + + (define-type-expander record + (syntax-parser + [(_ f:id … . {~or (#:ρ ρ:id) ρ:id}) + (define set + (free-id-table-ref! ρ-table #'ρ (λ () <make-lifo-set>))) + (for ([f (in-syntax #'(f …))]) + (<lifo-set-add!> set (syntax->datum f))) + #'(List 'f … 'ρ)])) + + (define-syntax ρ-inst + (syntax-parser + [(_ f A:id … #:ρ ρ:id …) + #'TODO]))] + +꩜chunk[<make-lifo-set> + ;; ordered free-identifier-set, last added = first in the order. + (cons (box '()) (mutable-set))] + +꩜chunk[<lifo-member?> + (λ (s v) (set-member? (cdr s) v))] + +꩜chunk[<lifo-set-add!> + (λ (s v) + (set-box! (car s) (cons v (unbox (car s)))) + (set-add! (cdr s) v))] + +꩜chunk[<lifo-set→list> + (λ (s) (unbox (car s)))] + ꩜section{Type of a record, with a single hole} In order to functionally update records, the updating functions will take a @@ -610,10 +738,15 @@ interesting subparts of the trees (those where there are fields). racket/syntax racket/list syntax/id-table + racket/set racket/sequence racket/vector - racket/contract) - (for-meta 2 racket/base)) + racket/contract + type-expander/expander) + (for-meta 2 racket/base) + (only-in phc-graph/xtyped lift-delayed-module-end-declaration) + (prefix-in tr: (only-in typed/racket ∀)) + racket/splicing) (provide (for-syntax define-trees) ;; For tests: @@ -621,7 +754,10 @@ interesting subparts of the trees (those where there are fields). ;;DEBUG: (for-syntax τ-tree-with-fields) - record-builder) + record-builder + ∀ρ + with-ρ + record) <maybe> <tree-type-with-replacement> @@ -633,7 +769,11 @@ interesting subparts of the trees (those where there are fields). <define-struct↔tree> <define-trees> <bt-fields-type> - (begin-for-syntax <idx→tree>) - <record-builder>] + (begin-for-syntax + <dichotomy> + <idx→tree>) + <empty-branches> + <record-builder> + <record-type>] ꩜include-section[(submod "flexible-with-utils.hl.rkt" doc)] \ No newline at end of file diff --git a/scribblings/collapsed-breakdown.png b/scribblings/collapsed-breakdown.png new file mode 100644 index 0000000..9c5b655 Binary files /dev/null and b/scribblings/collapsed-breakdown.png differ diff --git a/scribblings/collapsed-updated.png b/scribblings/collapsed-updated.png new file mode 100644 index 0000000..8e9bf8d Binary files /dev/null and b/scribblings/collapsed-updated.png differ diff --git a/scribblings/collapsed.png b/scribblings/collapsed.png new file mode 100644 index 0000000..d7fc52f Binary files /dev/null and b/scribblings/collapsed.png differ diff --git a/test/test-flexible-with2.rkt b/test/test-flexible-with2.rkt new file mode 100644 index 0000000..039c0c7 --- /dev/null +++ b/test/test-flexible-with2.rkt @@ -0,0 +1,20 @@ +#lang phc-graph/xtyped + +(require "../flexible-with2.hl.rkt" + phc-toolkit/typed-rackunit-extensions) + +(check-ann ((record-builder 4 1 2 4 7 14) 'a 'b 'c 'd 'e) + (Pairof + (Pairof + (Pairof (Pairof empty1/τ 'a) (Pairof 'b empty1/τ)) + (Pairof (Pairof 'c empty1/τ) (Pairof empty1/τ 'd))) + (Pairof empty4/τ (Pairof empty2/τ (Pairof 'e empty1/τ))))) + +(with-ρ (Row) + (define-type test + (∀ρ (A #:ρ Row) + (→ (List A Row) + (record a b . Row) + (List A Row) + (record a c . Row))))) + diff --git a/xtyped.rkt b/xtyped.rkt new file mode 100644 index 0000000..4acf866 --- /dev/null +++ b/xtyped.rkt @@ -0,0 +1,40 @@ +#lang racket + +(module reader syntax/module-reader + phc-graph/xtyped) + +(require type-expander/lang) + +(provide (rename-out [-#%module-begin #%module-begin]) + (except-out (all-from-out type-expander/lang) + #%module-begin) + (for-syntax lift-delayed-module-end-declaration)) + +(define-for-syntax lifted (make-parameter #f)) +(define-for-syntax (lift-delayed-module-end-declaration decl) + (set-box! (lifted) (cons decl (unbox (lifted)))) + (void)) + +(define-syntax (-#%module-begin-2 stx) + (syntax-case stx () + [(_ -mb . more) + (with-syntax ([((lifted-def ...) (pmb . rest)) + (parameterize ([lifted (box '())]) + (define expanded + (local-expand (datum->syntax + stx + `(,#'#%plain-module-begin . ,#'more) + stx + stx) + 'module-begin + '())) + (list (map syntax-local-introduce (unbox (lifted))) + expanded))]) + #`(begin + (define-type #,(datum->syntax #'-mb 'T) Integer) + lifted-def ... + . rest))])) +(define-syntax (-#%module-begin stx) + (syntax-case stx () + [(-mb . more) + #'(#%module-begin (-#%module-begin-2 -mb . more))]))