phc-adt-choices.scrbl (5239B)
1 #lang hyper-literate typed/racket 2 @(require scribble-enhanced/doc) 3 @doc-lib-setup 4 5 @title[#:style manual-doc-style 6 #:tag "choices" 7 #:tag-prefix "phc-adt/choices"]{Somewhat outdated 8 overview of the implementation choices for structures, graphs and passes} 9 10 @(chunks-toc-prefix 11 '("(lib phc-adt/scribblings/phc-adt-implementation.scrbl)" 12 "phc-adt/choices")) 13 14 @section[#:tag "type-system|structures"]{Structures} 15 16 Structures are represented as lists of key/value pairs. 17 @note{We need lists and can't use vectors (or hash tables) because the latter 18 are mutable in @code|{typed/racket}|, and the typing system has no guarantee 19 that accessing the same element twice will yield the same value (so occurence 20 typing can't narrow the type in branches of conditionnals).} 21 @note{Actually, we can use structs (they are immutable by default, and the 22 occurrence typing knows that). There are two problems with them. The first 23 problem is that we cannot have subtyping (although knowing all the structs 24 used in the program means we can just filter them and use a 25 @racket[(U S1 S2 …)]. The second problem is that in order to declare the 26 structs, we would need to be in a define-like environment (therefore making 27 anonymous structure types problematic). The second problem can be solved in 28 the same way as the first: if all the structs are known in advance, we can 29 pre-declare them in a shared file.} 30 31 @chunk[<example-simple-structure> 32 (define-type abc (List (Pairof 'a Number) 33 (Pairof 'b String) 34 (Pairof 'c (U 'x 'y)))) 35 36 (: make-abc (→ Number String (U 'x 'y) abc)) 37 (define (make-abc a b c) 38 (list (cons 'a a) (cons 'b b) (cons 'c c))) 39 (make-abc 1 "b" 'x)] 40 41 Occurrence typing works: 42 43 @chunk[<example-simple-structure-occurrence> 44 (: f (→ abc (U 'x #f))) 45 (define (f v) 46 (if (eq? 'x (cdr (cddr v))) 47 (cdr (cddr v)) 48 #f))] 49 50 @section{Passes, subtyping and tests} 51 52 Below is the definition of a function which works on 53 @tc[(structure [a Number] [b String] [c Boolean])], and returns the same 54 structure extended with a field @tc[[d Number]], but is only concerned with 55 fields @tc[a] and @tc[c], so tests don't need to provide a value for @tc[b]. 56 57 @chunk[<example-pass-which-extends-input> 58 (: pass-calc-d (∀ (TB) (→ (List (Pairof 'a Number) 59 (Pairof 'b TB) 60 (Pairof 'c Boolean)) 61 (List (Pairof 'a Number) 62 (Pairof 'b TB) 63 (Pairof 'c Boolean) 64 (Pairof 'd Number))))) 65 (define (pass-calc-d v) 66 (list (car v) ; a 67 (cadr v) ; b 68 (caddr v) ; c 69 (cons 'd (+ (cdar v) (if (cdaddr v) 0 1)))))] 70 71 The example above can be called to test it with a dummy value for @tc[b]: 72 73 @chunk[<example-pass-which-extends-input> 74 (pass-calc-d '((a . 1) (b . no-field) (c . #t)))] 75 76 But when called with a proper value for @tc[b], we get back the original 77 string as expected, and the type is correct: 78 79 @chunk[<example-pass-which-extends-input> 80 (ann (pass-calc-d '((a . 1) (b . "some string") (c . #t))) 81 (List (Pairof 'a Number) 82 (Pairof 'b String) 83 (Pairof 'c Boolean) 84 (Pairof 'd Number)))] 85 86 If the pass should be able to work on multiple graph types (each being a subtype 87 containing a common subset of fields), then it should be easy to mark it as a 88 @tc[case→] function. It is probably better to avoid too permissive subtyping, 89 otherwise, imagine we have 90 a pass which removes @tc[Addition]s and @tc[Substraction]s from an AST, and 91 replaces them with a single @tc[Arithmetic] node type. If we have full duck 92 typing, we could call it with @tc[Addition]s and @tc[Substraction] hidden in 93 fields it does not know about, and so it would fail to replace them. Also, it 94 could be called with an already-processed AST which already contains just 95 @tc[Arithmetic] node types, which would be a bug most likely. Therefore, 96 explicitly specifying the graph type on which the passes work seems a good 97 practice. Some parts can be collapsed easily into a @tc[∀] type @tc[T], when 98 we're sure there shouldn't be anything that interests us there. 99 100 @section{Graphs} 101 102 In order to be able to have cycles, while preserving the benefits of 103 occurrence typing, we need to make sure that from the type system's point of 104 view, accessing a successor node twice will return the same value each time. 105 106 The easiest way for achieving this is to wrap the to-be-created value inside a 107 @tc[Promise]. Occurrence typing works on those: 108 109 @chunk[<test-promise-occurence-typing> 110 (: test-promise-occurence (→ (Promise (U 'a 'b)) (U 'a #f))) 111 (define (test-promise-occurence p) 112 (if (eq? (force p) 'a) 113 (force p) 114 #f))] 115 116 @section{Conclusion} 117 118 @chunk[<*> 119 <example-simple-structure> 120 <example-simple-structure-occurrence> 121 122 <example-pass-which-extends-input> 123 124 <test-promise-occurence-typing>]