├── main.rkt ├── documentation ├── images │ ├── install_1.jpeg │ ├── install_2.jpeg │ ├── program_1.jpeg │ ├── program_2.jpeg │ ├── program_3.jpeg │ ├── racket_initial.jpeg │ └── racket_select_package.jpeg └── ranked_programming.pdf ├── examples ├── ranked_procedure_call.rkt ├── recursion.rkt ├── boolean_circuit.rkt ├── ranking_network.rkt ├── ranked_let.rkt ├── hidden_markov.rkt ├── localisation.rkt └── spelling_correction.rkt ├── info.rkt ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── LICENSE ├── scribblings ├── manual-racket.js ├── racket.css ├── scribble-common.js ├── manual-racket.css ├── scribble.css ├── manual-style.css └── ranked-programming.scrbl ├── README.md ├── rp-test.rkt ├── rp-core.rkt └── rp-api.rkt /main.rkt: -------------------------------------------------------------------------------- 1 | #lang racket/base 2 | 3 | (require "rp-api.rkt") 4 | (provide (all-from-out "rp-api.rkt")) 5 | 6 | -------------------------------------------------------------------------------- /documentation/images/install_1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjitze/ranked-programming/HEAD/documentation/images/install_1.jpeg -------------------------------------------------------------------------------- /documentation/images/install_2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjitze/ranked-programming/HEAD/documentation/images/install_2.jpeg -------------------------------------------------------------------------------- /documentation/images/program_1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjitze/ranked-programming/HEAD/documentation/images/program_1.jpeg -------------------------------------------------------------------------------- /documentation/images/program_2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjitze/ranked-programming/HEAD/documentation/images/program_2.jpeg -------------------------------------------------------------------------------- /documentation/images/program_3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjitze/ranked-programming/HEAD/documentation/images/program_3.jpeg -------------------------------------------------------------------------------- /documentation/ranked_programming.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjitze/ranked-programming/HEAD/documentation/ranked_programming.pdf -------------------------------------------------------------------------------- /documentation/images/racket_initial.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjitze/ranked-programming/HEAD/documentation/images/racket_initial.jpeg -------------------------------------------------------------------------------- /documentation/images/racket_select_package.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tjitze/ranked-programming/HEAD/documentation/images/racket_select_package.jpeg -------------------------------------------------------------------------------- /examples/ranked_procedure_call.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require ranked-programming) 3 | 4 | ; We calculate the sum of two values 5 | (pr ($ + 10 5)) 6 | 7 | ; Now we add uncertainty: the first value is normally 10 but exceptionally 20 8 | (pr ($ + (nrm/exc 10 20) 5)) 9 | 10 | ; More uncertainty: we normally add, but exceptionally subtract 11 | (pr ($ (nrm/exc + -) (nrm/exc 10 20) 5)) 12 | 13 | -------------------------------------------------------------------------------- /info.rkt: -------------------------------------------------------------------------------- 1 | #lang info 2 | (define collection "ranked-programming") 3 | (define deps '("sandbox-lib" 4 | "scribble-lib" 5 | "srfi-lite-lib" 6 | "base")) 7 | (define build-deps '("scribble-lib" "racket-doc" "rackunit-lib")) 8 | (define scribblings '(("scribblings/ranked-programming.scrbl" ()))) 9 | (define pkg-desc "Ranked Programming for Racket") 10 | (define version "1.0") 11 | (define pkg-authors '(tjitze)) 12 | -------------------------------------------------------------------------------- /examples/recursion.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require ranked-programming) 3 | 4 | ; The function (fun x) normally returns x and exceptionally (fun (* x 2)) 5 | (define (fun x) (nrm/exc x (fun (* x 2)))) 6 | 7 | ; We call (fun 1) and print the output. Note that (fun 1) yields an infinite 8 | ; ranking (i.e. assigns finite ranks to infinitely many values) but that pr 9 | ; only displays the 10 lowest ranked values. 10 | (pr (fun 1)) 11 | 12 | ; Now we call (fun 1) and observe that the value is greater than 100. 13 | (pr (observe (lambda (x) (> x 100)) (fun 1))) 14 | 15 | -------------------------------------------------------------------------------- /examples/boolean_circuit.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require ranked-programming) 3 | 4 | ; the boolean circuit example from the paper 5 | (define (circuit i1 i2 i3 o) 6 | ($ cdr 7 | (observe 8 | (lambda (x) (eq? (car x) o)) 9 | (rlet* ((N (nrm/exc #t #f)) 10 | (O1 (nrm/exc #t #f)) 11 | (O2 (nrm/exc #t #f)) 12 | (l1 (if N (not i1) #f)) 13 | (l2 (if O1 (or l1 i2) #f)) 14 | (out (if O2 (or l2 i3) #f))) 15 | (list out N O1 O2))))) 16 | 17 | ; most probable explanations for faulty output #f given input #f/#f/#t 18 | (pr (circuit #f #f #t #f)) -------------------------------------------------------------------------------- /examples/ranking_network.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require ranked-programming) 3 | 4 | ; the ranking network example from the paper 5 | (define (network) 6 | (rlet* 7 | ((H (nrm/exc #f #t 15)) 8 | (B (if H (nrm/exc #f #t 4) 9 | (nrm/exc #t #f 8))) 10 | (F (nrm/exc #t #f 10)) 11 | (S (cond [(and B F) (nrm/exc #t #f 3)] 12 | [(and B (not F)) (nrm/exc #f #t 13)] 13 | [(and (not B) F) (nrm/exc #f #t 11)] 14 | [else (nrm/exc #f #t 27)]))) 15 | (list H B F S))) 16 | 17 | ; evaluate complete network 18 | (pr (network)) 19 | 20 | ; ranking over S given that F is true 21 | (pr ($ fourth (observe third (network)))) -------------------------------------------------------------------------------- /examples/ranked_let.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require ranked-programming) 3 | 4 | ; Let p and b be boolean variables standing for "beer" and "peanuts". We 5 | ; only exceptionally drink beer, and thus p becomes: 6 | ; 7 | ; (nrm/exc #f #t). 8 | ; 9 | ; Our peanut consumption depends on whether we drink beer: if we do, we 10 | ; normally have peanuts, otherwise we don't. Thus b becomes: 11 | ; 12 | ; (if b (nrm/exc #t #f) #f). 13 | ; 14 | ; Note that this expression refers to b (p is dependent on b). We can 15 | ; express this with a ranked let expression. 16 | (pr (rlet* 17 | ((b (nrm/exc #f #t)) 18 | (p (if b (nrm/exc #t #f) #f))) 19 | (list "beer:" b "peanuts:" p))) 20 | 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /examples/hidden_markov.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require ranked-programming) 3 | 4 | ; the hidden markov model example from the paper 5 | 6 | ; A simple toy-example of a ranking-based hidden markov model specified through 7 | ; the parameters init, trans and emit. 8 | (define (init) 9 | (either/or "rainy" "sunny")) 10 | (define (trans s) 11 | (case s 12 | (("rainy") (nrm/exc "rainy" "sunny" 2)) 13 | (("sunny") (nrm/exc "sunny" "rainy" 2)))) 14 | (define (emit s) 15 | (case s 16 | (("rainy") (nrm/exc "yes" "no" 1)) 17 | (("sunny") (nrm/exc "no" "yes" 1)))) 18 | 19 | ; a generic implementation of a hidden markov model 20 | (define (hmm obs) 21 | (if (empty? obs) 22 | ($ list (init)) 23 | ($ cdr 24 | (observe (lambda (x) (eq? (car x) (car obs))) 25 | (rlet* ((p (hmm (cdr obs))) 26 | (s (trans (car p))) 27 | (o (emit s))) 28 | (cons o (cons s p))))))) 29 | 30 | ; Inference example 1 31 | (pr (hmm `("no" "no" "yes" "no" "no"))) 32 | 33 | ; Inference example 2 34 | (pr (hmm `("yes" "yes" "yes" "no" "no"))) 35 | 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Tjitze Rienstra 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/localisation.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require ranked-programming) 3 | 4 | ; the robot localisation example from the paper 5 | 6 | (define (neighbouring s) 7 | (let ((x (first s)) 8 | (y (second s))) 9 | (either-of (list (list (- x 1) y) 10 | (list (+ x 1) y) 11 | (list x (- y 1)) 12 | (list x (+ y 1)))))) 13 | 14 | (define (surrounding s) 15 | (let ((x (first s)) 16 | (y (second s))) 17 | (either-of (list (list (- x 1) (- y 1)) 18 | (list (- x 0) (- y 1)) 19 | (list (+ x 1) (- y 1)) 20 | (list (+ x 1) (- y 0)) 21 | (list (+ x 1) (+ y 1)) 22 | (list (- x 0) (+ y 1)) 23 | (list (- x 1) (+ y 1)) 24 | (list (- x 1) (- y 0)))))) 25 | 26 | (define (init) (list 0 0)) 27 | 28 | (define (next-state s) (neighbouring s)) 29 | 30 | (define (observable s) (nrm/exc s (surrounding s))) 31 | 32 | ; a generic implementation of a hidden markov model 33 | (define (hmm obs) 34 | (if (empty? obs) 35 | ($ list (init)) 36 | ($ cdr 37 | (observe (lambda (x) (equal? (car x) (car obs))) 38 | (rlet* ((p (hmm (cdr obs))) 39 | (s (next-state (car p))) 40 | (o (observable s))) 41 | (cons o (cons s p))))))) 42 | 43 | ; Inference example 1 44 | (pr (hmm `((0 3) (2 3) (3 3) (3 2) (4 1) (2 1) (3 0) (1 0)))) 45 | 46 | 47 | -------------------------------------------------------------------------------- /examples/spelling_correction.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | (require ranked-programming) 3 | 4 | ; Read dictionary 5 | (define (read-file filename) 6 | (with-input-from-file filename 7 | (λ () 8 | (for/list ([line (in-lines)]) 9 | line)))) 10 | 11 | (define dictionary (map string->list (read-file "google-10000-english-no-swears.txt"))) 12 | 13 | ; Below is an implementation of the "match" function mentioned in the paper that 14 | ; is based on a trie encoding of the dictionary, with lookup supporting wildcards. 15 | 16 | ; An empty trie 17 | (define empty-trie (cons `() `())) 18 | 19 | ; Add key/value to trie 20 | (define (add-to-trie trie key value) 21 | (cond 22 | [(empty? key) (cons (car trie) (list value))] 23 | [(assoc (car key) (car trie)) 24 | (cons (dict-set (car trie) (car key) (add-to-trie (cdr (assoc (car key) (car trie))) (cdr key) value)) (cdr trie))] 25 | [else (cons (dict-set (car trie) (car key) (add-to-trie empty-trie (cdr key) value)) (cdr trie))])) 26 | 27 | ; Add dictionary to tree (each word mapped to itself) 28 | (define (add-all-to-trie trie dict) 29 | (if (empty? dict) trie (add-to-trie (add-all-to-trie trie (cdr dict)) (car dict) (car dict)))) 30 | 31 | ; Create the trie for the dictionary 32 | (define dict-trie (add-all-to-trie empty-trie dictionary)) 33 | 34 | ; Lookup value for given key in a set of tries 35 | (define (lookup* tries word) 36 | (if (empty? word) 37 | (append* (map cdr tries)) 38 | (if (equal? (car word) #\*) 39 | (lookup* (map cdr (append* (map car tries))) (cdr word)) 40 | (lookup* (map cdr (filter (lambda (x) (equal? (car x) (car word))) (append* (map car tries)))) (cdr word))))) 41 | 42 | ; Take pattern as input, return list of strings that match pattern (* for wildcard) 43 | (define (match pattern) (lookup* (list dict-trie) pattern)) 44 | 45 | ; The gen function from the paper 46 | (define (gen input) 47 | (if (empty? input) 48 | `() 49 | (nrm/exc ($ cons (car input) (gen (cdr input))) 50 | (either/or (gen (cdr input)) 51 | ($ cons #\* (gen (cdr input))) 52 | ($ cons #\* (gen input)))))) 53 | 54 | ; The correct function from the paper 55 | (define (correct input) 56 | ($ match 57 | (observe 58 | (lambda (x) (not (empty? (match x)))) 59 | (gen (string->list input))))) 60 | 61 | ; example 62 | (pr-first 2 (correct "swtzerlandd")) 63 | 64 | -------------------------------------------------------------------------------- /scribblings/manual-racket.js: -------------------------------------------------------------------------------- 1 | /* For the Racket manual style */ 2 | 3 | AddOnLoad(function() { 4 | /* Look for header elements that have x-source-module and x-part tag. 5 | For those elements, add a hidden element that explains how to 6 | link to the section, and set the element's onclick() to display 7 | the explanation. */ 8 | var tag_names = ["h1", "h2", "h3", "h4", "h5"]; 9 | for (var j = 0; j < tag_names.length; j++) { 10 | elems = document.getElementsByTagName(tag_names[j]); 11 | for (var i = 0; i < elems.length; i++) { 12 | var elem = elems.item(i); 13 | AddPartTitleOnClick(elem); 14 | } 15 | } 16 | }) 17 | 18 | function AddPartTitleOnClick(elem) { 19 | var mod_path = elem.getAttribute("x-source-module"); 20 | var tag = elem.getAttribute("x-part-tag"); 21 | if (mod_path && tag) { 22 | // Might not be present: 23 | var prefixes = elem.getAttribute("x-part-prefixes"); 24 | 25 | var info = document.createElement("div"); 26 | info.className = "RPartExplain"; 27 | 28 | /* The "top" tag refers to a whole document: */ 29 | var is_top = (tag == "\"top\""); 30 | info.appendChild(document.createTextNode("Link to this " 31 | + (is_top ? "document" : "section") 32 | + " with ")); 33 | 34 | /* Break `secref` into two lines if the module path and tag 35 | are long enough: */ 36 | var is_long = (is_top ? false : ((mod_path.length 37 | + tag.length 38 | + (prefixes ? (16 + prefixes.length) : 0)) 39 | > 60)); 40 | 41 | var line1 = document.createElement("div"); 42 | var line1x = ((is_long && prefixes) ? document.createElement("div") : line1); 43 | var line2 = (is_long ? document.createElement("div") : line1); 44 | 45 | function add(dest, str, cn) { 46 | var s = document.createElement("span"); 47 | s.className = cn; 48 | s.style.whiteSpace = "nowrap"; 49 | s.appendChild(document.createTextNode(str)); 50 | dest.appendChild(s); 51 | } 52 | /* Construct a `secref` call with suitable syntax coloring: */ 53 | add(line1, "\xA0@", "RktRdr"); 54 | add(line1, (is_top ? "other-doc" : "secref"), "RktSym"); 55 | add(line1, "[", "RktPn"); 56 | if (!is_top) 57 | add(line1, tag, "RktVal"); 58 | if (is_long) { 59 | /* indent additional lines: */ 60 | if (prefixes) 61 | add(line1x, "\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0", "RktPn"); 62 | add(line2, "\xA0\xA0\xA0\xA0\xA0\xA0\xA0\xA0", "RktPn"); 63 | } 64 | if (prefixes) { 65 | add(line1x, " #:tag-prefixes ", "RktPn"); 66 | add(line1x, "'", "RktVal"); 67 | add(line1x, prefixes, "RktVal"); 68 | } 69 | if (!is_top) 70 | add(line2, " #:doc ", "RktPn"); 71 | add(line2, "'", "RktVal"); 72 | add(line2, mod_path, "RktVal"); 73 | add(line2, "]", "RktPn"); 74 | 75 | info.appendChild(line1); 76 | if (is_long) 77 | info.appendChild(line1x); 78 | if (is_long) 79 | info.appendChild(line2); 80 | 81 | info.style.display = "none"; 82 | 83 | /* Add the new element afterthe header: */ 84 | var n = elem.nextSibling; 85 | if (n) 86 | elem.parentNode.insertBefore(info, n); 87 | else 88 | elem.parentNode.appendChild(info); 89 | 90 | /* Clicking the header shows the explanation element: */ 91 | elem.onclick = function () { 92 | if (info.style.display == "none") 93 | info.style.display = "block"; 94 | else 95 | info.style.display = "none"; 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /scribblings/racket.css: -------------------------------------------------------------------------------- 1 | 2 | /* See the beginning of "scribble.css". */ 3 | 4 | /* Monospace: */ 5 | .RktIn, .RktRdr, .RktPn, .RktMeta, 6 | .RktMod, .RktKw, .RktVar, .RktSym, 7 | .RktRes, .RktOut, .RktCmt, .RktVal, 8 | .RktBlk { 9 | font-family: monospace; 10 | white-space: inherit; 11 | } 12 | 13 | /* Serif: */ 14 | .inheritedlbl { 15 | font-family: serif; 16 | } 17 | 18 | /* Sans-serif: */ 19 | .RBackgroundLabelInner { 20 | font-family: sans-serif; 21 | } 22 | 23 | /* ---------------------------------------- */ 24 | /* Inherited methods, left margin */ 25 | 26 | .inherited { 27 | width: 100%; 28 | margin-top: 0.5em; 29 | text-align: left; 30 | background-color: #ECF5F5; 31 | } 32 | 33 | .inherited td { 34 | font-size: 82%; 35 | padding-left: 1em; 36 | text-indent: -0.8em; 37 | padding-right: 0.2em; 38 | } 39 | 40 | .inheritedlbl { 41 | font-style: italic; 42 | } 43 | 44 | /* ---------------------------------------- */ 45 | /* Racket text styles */ 46 | 47 | .RktIn { 48 | color: #cc6633; 49 | background-color: #eeeeee; 50 | } 51 | 52 | .RktInBG { 53 | background-color: #eeeeee; 54 | } 55 | 56 | .RktRdr { 57 | } 58 | 59 | .RktPn { 60 | color: #843c24; 61 | } 62 | 63 | .RktMeta { 64 | color: black; 65 | } 66 | 67 | .RktMod { 68 | color: black; 69 | } 70 | 71 | .RktOpt { 72 | color: black; 73 | } 74 | 75 | .RktKw { 76 | color: black; 77 | } 78 | 79 | .RktErr { 80 | color: red; 81 | font-style: italic; 82 | } 83 | 84 | .RktVar { 85 | color: #262680; 86 | font-style: italic; 87 | } 88 | 89 | .RktSym { 90 | color: #262680; 91 | } 92 | 93 | .RktSymDef { /* used with RktSym at def site */ 94 | } 95 | 96 | .RktValLink { 97 | text-decoration: none; 98 | color: blue; 99 | } 100 | 101 | .RktValDef { /* used with RktValLink at def site */ 102 | } 103 | 104 | .RktModLink { 105 | text-decoration: none; 106 | color: blue; 107 | } 108 | 109 | .RktStxLink { 110 | text-decoration: none; 111 | color: black; 112 | } 113 | 114 | .RktStxDef { /* used with RktStxLink at def site */ 115 | } 116 | 117 | .RktRes { 118 | color: #0000af; 119 | } 120 | 121 | .RktOut { 122 | color: #960096; 123 | } 124 | 125 | .RktCmt { 126 | color: #c2741f; 127 | } 128 | 129 | .RktVal { 130 | color: #228b22; 131 | } 132 | 133 | /* ---------------------------------------- */ 134 | /* Some inline styles */ 135 | 136 | .together { 137 | width: 100%; 138 | } 139 | 140 | .prototype, .argcontract, .RBoxed { 141 | white-space: nowrap; 142 | } 143 | 144 | .prototype td { 145 | vertical-align: text-top; 146 | } 147 | 148 | .RktBlk { 149 | white-space: inherit; 150 | text-align: left; 151 | } 152 | 153 | .RktBlk tr { 154 | white-space: inherit; 155 | } 156 | 157 | .RktBlk td { 158 | vertical-align: baseline; 159 | white-space: inherit; 160 | } 161 | 162 | .argcontract td { 163 | vertical-align: text-top; 164 | } 165 | 166 | .highlighted { 167 | background-color: #ddddff; 168 | } 169 | 170 | .defmodule { 171 | width: 100%; 172 | background-color: #F5F5DC; 173 | } 174 | 175 | .specgrammar { 176 | float: right; 177 | } 178 | 179 | .RBibliography td { 180 | vertical-align: text-top; 181 | } 182 | 183 | .leftindent { 184 | margin-left: 1em; 185 | margin-right: 0em; 186 | } 187 | 188 | .insetpara { 189 | margin-left: 1em; 190 | margin-right: 1em; 191 | } 192 | 193 | .Rfilebox { 194 | } 195 | 196 | .Rfiletitle { 197 | text-align: right; 198 | margin: 0em 0em 0em 0em; 199 | } 200 | 201 | .Rfilename { 202 | border-top: 1px solid #6C8585; 203 | border-right: 1px solid #6C8585; 204 | padding-left: 0.5em; 205 | padding-right: 0.5em; 206 | background-color: #ECF5F5; 207 | } 208 | 209 | .Rfilecontent { 210 | margin: 0em 0em 0em 0em; 211 | } 212 | 213 | .RpackageSpec { 214 | padding-right: 0.5em; 215 | } 216 | 217 | /* ---------------------------------------- */ 218 | /* For background labels */ 219 | 220 | .RBackgroundLabel { 221 | float: right; 222 | width: 0px; 223 | height: 0px; 224 | } 225 | 226 | .RBackgroundLabelInner { 227 | position: relative; 228 | width: 25em; 229 | left: -25.5em; 230 | top: 0px; 231 | text-align: right; 232 | color: white; 233 | z-index: 0; 234 | font-weight: bold; 235 | } 236 | 237 | .RForeground { 238 | position: relative; 239 | left: 0px; 240 | top: 0px; 241 | z-index: 1; 242 | } 243 | 244 | /* ---------------------------------------- */ 245 | /* History */ 246 | 247 | .SHistory { 248 | font-size: 82%; 249 | } 250 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ranked Programming 2 | 3 | This repository contains [the ranked-programming package](https://pkgs.racket-lang.org/package/ranked-programming) for use with [Racket](https://racket-lang.org). Racket is a programming language based on the Scheme dialect of LISP. The ranked-programming package is an implementation of the approach described in the paper [Ranked Programming](https://github.com/tjitze/ranked-programming/blob/master/documentation/ranked_programming.pdf). This paper was presented at the IJCAI 2019 conference (August 10-16, Macao, China). 4 | 5 | In short, ranked programming is similar to [probabilistic programming](http://probabilistic-programming.org/wiki/Home), except that the underlying uncertainty formalism is replaced with ranking theory, which measures uncertainty using degrees of surprise on the integer scale from 0 to ∞. Like probabilistic programming, ranked programming provides a simple and flexible way to represent models with uncertain behaviour, and to perform inference with such models. However, instead of using probabilities, one expresses uncertainty by specifying what happens "normally" and what happens "exceptionally". For a more detailed description, please read the [paper](https://github.com/tjitze/ranked-programming/blob/master/documentation/ranked_programming.pdf). 6 | 7 | A reference manual for this package can be found [here](http://docs.racket-lang.org/ranked-programming@ranked-programming/index.html). 8 | 9 | In this document we provide some instructions to get started with ranked programming in Racket. For a more general introduction to Racket please consult [Racket's own getting started](https://docs.racket-lang.org/getting-started/) guide. 10 | 11 | # Getting Started 12 | 13 | ## Install Racket 14 | 15 | The ranked programming library is written for use with the *Racket* programming language, which is based on the Scheme dialect of LISP. To get started, we first need to download and install Racket, which includes an editor called *DrRacket*. The download can be obtained [here](https://download.racket-lang.org). 16 | 17 | ## Install the Ranked Programming Package 18 | 19 | The ranked programming library is distributed as a package and can be installed through DrRacket's package manager. To do so, open the DrRacket editor, open the *File* dropdown menu, and select *Package Manager*. In the Package Manager window, select the *Available from Catalog* tab, and click *Update Package List*. Then find and select the package called *ranked-programming* and click *Install*. 20 | 21 | 22 | 23 | When done we can close the package manager window. 24 | 25 | ## Defining Your First Program 26 | 27 | The DrRacket window has two text panels: the definitions panel (top half) and the interactions panel (bottom half). The purpose of the definitions panel is to define your program. Let's give a simple example. Any program starts by specifying its language. We use the Racket language, so we say `#lang racket`. In addition, we must say that we require the package called ranked-programming: `(require ranked-programming)`. We can then define our program. When we click *Run*, the output of our program is displayed in the interactions panel. 28 | 29 | 30 | 31 | Note that the syntax used here differs from the syntax of the language described in the paper. See the [reference](http://docs.racket-lang.org/ranked-programming@ranked-programming/index.html) for details. 32 | 33 | ## Interactive Evaluation of Expressions 34 | 35 | Apart from displaying the output of a program, the interactions panel can be used to evaluate expressions interactively. This is useful for debugging purposes. It's also useful if we just want to experiment with the language. We can use the program we entered above as a starting point. Click *Run* and enter an expression (for example `(pr (nrm/exc "A" "B"))`) in the interactions panel. 36 | 37 | 38 | 39 | Furthermore, all the definitions of our program are available when evaluating expressions in the interaction panel. In the example below we interactively enter an expression which calls `(count 1)` and observes that the output is greater than 100. 40 | 41 | 42 | 43 | ## Running examples 44 | 45 | All the examples from the paper are included in the [examples](https://github.com/tjitze/ranked-programming/blob/master/examples) directory. These are: 46 | 47 | * [recursion.rkt](https://github.com/tjitze/ranked-programming/blob/master/examples/recursion.rkt) An example of a recursively defined ranking. 48 | * [ranked_procedure_call.rkt](https://github.com/tjitze/ranked-programming/blob/master/examples/ranked_procedure_call.rkt) An example using the ranked procedure call. 49 | * [ranked_let.rkt](https://github.com/tjitze/ranked-programming/blob/master/examples/ranked_let.rkt) An example using the ranked let statement. 50 | * [ranking_network.rkt](https://github.com/tjitze/ranked-programming/blob/master/examples/ranking_network.rkt) A ranking network. 51 | * [boolean_circuit.rkt](https://github.com/tjitze/ranked-programming/blob/master/examples/boolean_circuit.rkt) The boolean circuit diagnosis example. 52 | * [hidden_markov.rkt](https://github.com/tjitze/ranked-programming/blob/master/examples/hidden_markov.rkt) The generic hidden markov model implementation + umbrella example. 53 | * [spelling_correction.rkt](https://github.com/tjitze/ranked-programming/blob/master/examples/spelling_correction.rkt) Spelling correction (using [google-10000-english-no-swears.txt](https://github.com/tjitze/ranked-programming/blob/master/examples/google-10000-english-no-swears.txt) as dictionary). 54 | 55 | These examples can be downloaded and opened (or copy-pasted into the definitions panel) directly in the DrRacket editor. 56 | 57 | 58 | # Contact 59 | 60 | Please contact me if you have questions or suggestions (tjitze@gmail.com). Contributions are welcome. 61 | 62 | # License 63 | 64 | This software is licensed under the MIT license. 65 | -------------------------------------------------------------------------------- /scribblings/scribble-common.js: -------------------------------------------------------------------------------- 1 | // Common functionality for PLT documentation pages 2 | 3 | // Page Parameters ------------------------------------------------------------ 4 | 5 | var page_query_string = location.search.substring(1); 6 | 7 | var page_args = 8 | ((function(){ 9 | if (!page_query_string) return []; 10 | var args = page_query_string.split(/[&;]/); 11 | for (var i=0; i= 0) args[i] = [a.substring(0,p), a.substring(p+1)]; 15 | else args[i] = [a, false]; 16 | } 17 | return args; 18 | })()); 19 | 20 | function GetPageArg(key, def) { 21 | for (var i=0; i= 0 && cur.substring(0,eql) == key) 78 | return unescape(cur.substring(eql+1)); 79 | } 80 | return def; 81 | } 82 | } 83 | 84 | function SetCookie(key, val) { 85 | try { 86 | localStorage[key] = val; 87 | } catch(e) { 88 | var d = new Date(); 89 | d.setTime(d.getTime()+(365*24*60*60*1000)); 90 | try { 91 | document.cookie = 92 | key + "=" + escape(val) + "; expires="+ d.toGMTString() + "; path=/"; 93 | } catch (e) {} 94 | } 95 | } 96 | 97 | // note that this always stores a directory name, ending with a "/" 98 | function SetPLTRoot(ver, relative) { 99 | var root = location.protocol + "//" + location.host 100 | + NormalizePath(location.pathname.replace(/[^\/]*$/, relative)); 101 | SetCookie("PLT_Root."+ver, root); 102 | } 103 | 104 | // adding index.html works because of the above 105 | function GotoPLTRoot(ver, relative) { 106 | var u = GetCookie("PLT_Root."+ver, null); 107 | if (u == null) return true; // no cookie: use plain up link 108 | // the relative path is optional, default goes to the toplevel start page 109 | if (!relative) relative = "index.html"; 110 | location = u + relative; 111 | return false; 112 | } 113 | 114 | // Utilities ------------------------------------------------------------------ 115 | 116 | var normalize_rxs = [/\/\/+/g, /\/\.(\/|$)/, /\/[^\/]*\/\.\.(\/|$)/]; 117 | function NormalizePath(path) { 118 | var tmp, i; 119 | for (i = 0; i < normalize_rxs.length; i++) 120 | while ((tmp = path.replace(normalize_rxs[i], "/")) != path) path = tmp; 121 | return path; 122 | } 123 | 124 | // `noscript' is problematic in some browsers (always renders as a 125 | // block), use this hack instead (does not always work!) 126 | // document.write(""); 127 | 128 | // Interactions --------------------------------------------------------------- 129 | 130 | function DoSearchKey(event, field, ver, top_path) { 131 | var val = field.value; 132 | if (event && event.keyCode == 13) { 133 | var u = GetCookie("PLT_Root."+ver, null); 134 | if (u == null) u = top_path; // default: go to the top path 135 | u += "search/index.html?q=" + encodeURIComponent(val); 136 | u = MergePageArgsIntoUrl(u); 137 | location = u; 138 | return false; 139 | } 140 | return true; 141 | } 142 | 143 | function TocviewToggle(glyph, id) { 144 | var s = document.getElementById(id).style; 145 | var expand = s.display == "none"; 146 | s.display = expand ? "block" : "none"; 147 | glyph.innerHTML = expand ? "▼" : "►"; 148 | } 149 | 150 | // Page Init ------------------------------------------------------------------ 151 | 152 | // Note: could make a function that inspects and uses window.onload to chain to 153 | // a previous one, but this file needs to be required first anyway, since it 154 | // contains utilities for all other files. 155 | var on_load_funcs = []; 156 | function AddOnLoad(fun) { on_load_funcs.push(fun); } 157 | window.onload = function() { 158 | for (var i=0; i tr:first-child > td > .together { 185 | border-top: 0px; /* erase border on first instance of together */ 186 | } 187 | 188 | .RktBlk { 189 | white-space: pre; 190 | text-align: left; 191 | } 192 | 193 | .highlighted { 194 | font-size: 1rem; 195 | background-color: #fee; 196 | } 197 | 198 | .defmodule { 199 | font-family: 'Fira-Mono', monospace; 200 | padding: 0.25rem 0.75rem 0.25rem 0.5rem; 201 | margin-bottom: 1rem; 202 | width: 100%; 203 | background-color: #ebf0f4; 204 | } 205 | 206 | .defmodule a { 207 | color: #444; 208 | } 209 | 210 | 211 | .defmodule td span.hspace:first-child { 212 | position: absolute; 213 | width: 0; 214 | display: inline-block; 215 | } 216 | 217 | .defmodule .RpackageSpec .Smaller, 218 | .defmodule .RpackageSpec .stt { 219 | font-size: 1rem; 220 | } 221 | 222 | /* make parens ordinary color in defmodule */ 223 | .defmodule .RktPn { 224 | color: inherit; 225 | } 226 | 227 | .specgrammar { 228 | float: none; 229 | padding-left: 1em; 230 | } 231 | 232 | 233 | .RBibliography td { 234 | vertical-align: text-top; 235 | padding-top: 1em; 236 | } 237 | 238 | .leftindent { 239 | margin-left: 2rem; 240 | margin-right: 0em; 241 | } 242 | 243 | .insetpara { 244 | margin-left: 1em; 245 | margin-right: 1em; 246 | } 247 | 248 | .SCodeFlow .Rfilebox { 249 | margin-left: -1em; /* see 17.2 of guide, module languages */ 250 | } 251 | 252 | .Rfiletitle { 253 | text-align: right; 254 | background-color: #eee; 255 | } 256 | 257 | .SCodeFlow .Rfiletitle { 258 | border-top: 1px dotted gray; 259 | border-right: 1px dotted gray; 260 | } 261 | 262 | 263 | .Rfilename { 264 | border-top: 0; 265 | border-right: 0; 266 | padding-left: 0.5em; 267 | padding-right: 0.5em; 268 | background-color: inherit; 269 | } 270 | 271 | .Rfilecontent { 272 | margin: 0.5em; 273 | } 274 | 275 | .RpackageSpec { 276 | padding-right: 0; 277 | } 278 | 279 | /* ---------------------------------------- */ 280 | /* For background labels */ 281 | 282 | .RBackgroundLabel { 283 | float: right; 284 | width: 0px; 285 | height: 0px; 286 | } 287 | 288 | .RBackgroundLabelInner { 289 | position: relative; 290 | width: 25em; 291 | left: -25.5em; 292 | top: 0.20rem; /* sensitive to monospaced font choice */ 293 | text-align: right; 294 | z-index: 0; 295 | font-weight: 300; 296 | font-family: 'Fira-Mono', monospace; 297 | font-size: 0.9rem; 298 | color: gray; 299 | } 300 | 301 | 302 | .RpackageSpec .Smaller { 303 | font-weight: 300; 304 | font-family: 'Fira-Mono', monospace; 305 | font-size: 0.9rem; 306 | } 307 | 308 | .RForeground { 309 | position: relative; 310 | left: 0px; 311 | top: 0px; 312 | z-index: 1; 313 | } 314 | 315 | /* ---------------------------------------- */ 316 | /* For section source modules & tags */ 317 | 318 | .RPartExplain { 319 | background: #eee; 320 | font-size: 0.9rem; 321 | margin-top: 0.2rem; 322 | padding: 0.2rem; 323 | text-align: left; 324 | } 325 | -------------------------------------------------------------------------------- /rp-test.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | ;(require "main.rkt") 4 | (require "rp-api.rkt") 5 | 6 | (module+ test 7 | 8 | (require rackunit) 9 | 10 | ; rf-equal as check 11 | (define-simple-check (check-rf-equal? r-exp1 r-exp2) 12 | (rf-equal? r-exp1 r-exp2)) 13 | 14 | (test-case 15 | "nrm/exc" 16 | (check-rf-equal? (nrm/exc 1 2 1) 17 | (construct-ranking (1 . 0) (2 . 1))) 18 | (check-rf-equal? (nrm/exc 1 2 2) 19 | (construct-ranking (1 . 0) (2 . 2))) 20 | (check-rf-equal? (nrm/exc 1 1 1) 21 | (construct-ranking (1 . 0))) 22 | (check-rf-equal? (nrm/exc 1 (nrm/exc 2 3 1) 1) 23 | (construct-ranking (1 . 0) (2 . 1) (3 . 2))) 24 | (check-rf-equal? (nrm/exc (nrm/exc 10 20 5) 2 1) 25 | (construct-ranking (10 . 0) (2 . 1) (20 . 5)))) 26 | 27 | (test-case 28 | "either-of" 29 | (check-rf-equal? (either-of (list 1 2 3)) 30 | (construct-ranking (1 . 0) (2 . 0) (3 . 0))) 31 | (check-rf-equal? (either-of `()) 32 | (failure))) 33 | 34 | (test-case 35 | "either/or test" 36 | (check-rf-equal? (either/or) 37 | (failure)) 38 | (check-rf-equal? (either/or 1) 39 | (construct-ranking (1 . 0))) 40 | (check-rf-equal? (either/or 1 2) 41 | (construct-ranking (1 . 0) (2 . 0))) 42 | (check-rf-equal? (either/or 1 2 3) 43 | (construct-ranking (1 . 0) (2 . 0) (3 . 0))) 44 | (check-rf-equal? (either/or (nrm/exc 1 2 1) (nrm/exc 10 20 10)) 45 | (construct-ranking (1 . 0) (10 . 0) (2 . 1) (20 . 10)))) 46 | 47 | (test-case 48 | "observe" 49 | (check-rf-equal? (observe odd? (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3))) 50 | (construct-ranking (1 . 0) (3 . 2))) 51 | (check-rf-equal? (observe even? (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3))) 52 | (construct-ranking (0 . 0) (2 . 2))) 53 | (check-rf-equal? (observe (lambda (x) #F) (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3))) 54 | (failure)) 55 | (check-rf-equal? (observe (lambda (x) #T) (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3))) 56 | (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3)))) 57 | 58 | (test-case 59 | "observe-r" 60 | (check-rf-equal? (observe-r 100 odd? (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3))) 61 | (construct-ranking (1 . 0) (3 . 2) (0 . 100) (2 . 102))) 62 | (check-rf-equal? (observe-r 100 even? (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3))) 63 | (construct-ranking (0 . 0) (2 . 2) (1 . 100) (3 . 102))) 64 | (check-rf-equal? (observe-r 100 (lambda (x) #F) (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3))) 65 | (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3))) 66 | (check-rf-equal? (observe-r 100 (lambda (x) #T) (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3))) 67 | (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3)))) 68 | 69 | (test-case 70 | "observe-e" 71 | (check-rf-equal? (observe-e 100 odd? (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3))) 72 | (construct-ranking (1 . 0) (3 . 2) (0 . 99) (2 . 101))) 73 | (check-rf-equal? (observe-e 100 even? (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3))) 74 | (construct-ranking (0 . 0) (2 . 2) (1 . 101) (3 . 103))) 75 | (check-rf-equal? (observe-e 100 (lambda (x) #F) (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3))) 76 | (failure)) 77 | (check-rf-equal? (observe-e 100 (lambda (x) #T) (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3))) 78 | (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3)))) 79 | 80 | (test-case 81 | "cut" 82 | (check-rf-equal? (cut 0 (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3))) 83 | (construct-ranking (0 . 0))) 84 | (check-rf-equal? (cut 1 (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3))) 85 | (construct-ranking (0 . 0) (1 . 1))) 86 | (check-rf-equal? (cut 3 (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3))) 87 | (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3)))) 88 | 89 | (test-case 90 | "rank-of" 91 | (check-equal? (rank-of odd? (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3))) 1) 92 | (check-equal? (rank-of even? (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3))) 0) 93 | (check-equal? (rank-of (lambda (x) (> x 2)) (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3))) 3) 94 | (check-true (infinite? (rank-of (lambda (x) #F) (construct-ranking (0 . 0) (1 . 1) (2 . 2) (3 . 3)))))) 95 | 96 | (test-case 97 | "ranked application" 98 | (check-rf-equal? ($ + 10 5) 99 | (construct-ranking (15 . 0))) 100 | (check-rf-equal? ($ (nrm/exc + - 1) 10 5) 101 | (construct-ranking (15 . 0) (5 . 1))) 102 | (check-rf-equal? ($ + (nrm/exc 10 20 1) 5) 103 | (construct-ranking (15 . 0) (25 . 1))) 104 | (check-rf-equal? ($ + (nrm/exc 10 20 1) (nrm/exc 5 50 2)) 105 | (construct-ranking (15 . 0) (25 . 1) (60 . 2) (70 . 3))) 106 | (check-rf-equal? ($ (nrm/exc + - 1) (nrm/exc 10 20 1) (nrm/exc 5 50 2)) 107 | (construct-ranking (15 . 0) (25 . 1) (5 . 1) (60 . 2) (15 . 2) (70 . 3) (-40 . 3) (-30 . 4))) 108 | (check-rf-equal? ($ + (nrm/exc 10 20 1) (nrm/exc 5 (nrm/exc 50 500 2) 2)) 109 | (construct-ranking (15 . 0) (25 . 1) (60 . 2) (70 . 3) (510 . 4) (520 . 5)))) 110 | 111 | (test-case 112 | "rlet" 113 | (check-rf-equal? (rlet ((x (nrm/exc 1 2 1))) x) 114 | (construct-ranking (1 . 0) (2 . 1))) 115 | (check-rf-equal? (rlet ((x (nrm/exc 1 2 1)) (y (nrm/exc 10 20 2))) (list x y)) 116 | (construct-ranking ((1 10) . 0) ((2 10) . 1) ((1 20) . 2) ((2 20) . 3))) 117 | (check-rf-equal? (rlet ((x (nrm/exc 1 2 1)) (y (failure))) (list x y)) 118 | (failure))) 119 | 120 | (test-case 121 | "rlet*" 122 | (check-rf-equal? (rlet* ((x (nrm/exc 1 2 1))) x) 123 | (construct-ranking (1 . 0) (2 . 1))) 124 | (check-rf-equal? (rlet* ((x (nrm/exc #F #T 1)) (y (if x (nrm/exc 1 2 1) 0))) (list x y)) 125 | (construct-ranking ((#F 0) . 0) ((#T 1) . 1) ((#T 2) . 2))) 126 | (check-rf-equal? (rlet* ((x (nrm/exc 1 2 1)) (y (failure))) (list x y)) 127 | (failure))) 128 | 129 | (test-case 130 | "rf-equal" 131 | (check-true (rf-equal? (failure) (failure))) 132 | (check-true (rf-equal? (construct-ranking (0 . 0)) (construct-ranking (0 . 0)))) 133 | (check-true (rf-equal? (construct-ranking (0 . 0) (1 . 1)) (construct-ranking (0 . 0) (1 . 1)))) 134 | (check-false (rf-equal? (construct-ranking (0 . 0) (1 . 1)) (construct-ranking (0 . 0) (1 . 2)))) 135 | (check-false (rf-equal? (construct-ranking (0 . 0) (1 . 1)) (construct-ranking (0 . 0) (2 . 1)))) 136 | (check-false (rf-equal? (construct-ranking (0 . 0) (1 . 1)) (construct-ranking (0 . 0)))) 137 | (check-false (rf-equal? (construct-ranking (0 . 0)) (construct-ranking (0 . 0) (1 . 1)))) 138 | (check-false (rf-equal? (construct-ranking (0 . 0)) (failure)))) 139 | 140 | (test-case 141 | "rf->hash" 142 | (let ((hash1 (rf->hash (failure))) 143 | (hash2 (rf->hash (construct-ranking (1 . 0)))) 144 | (hash3 (rf->hash (construct-ranking (1 . 0) (2 . 1)))) 145 | (hash4 (rf->hash (construct-ranking (1 . 0) (2 . 1) (2 . 2))))) 146 | (check-equal? (hash-count hash1) 0) 147 | (check-equal? (hash-count hash2) 1) 148 | (check-equal? (hash-count hash3) 2) 149 | (check-equal? (hash-count hash4) 2) 150 | (check-equal? (hash-ref hash2 1) 0) 151 | (check-equal? (hash-ref hash3 1) 0) 152 | (check-equal? (hash-ref hash3 2) 1) 153 | (check-equal? (hash-ref hash4 1) 0) 154 | (check-equal? (hash-ref hash4 2) 1))) 155 | 156 | (test-case 157 | "rf->assoc" 158 | (let ((r1 (failure)) 159 | (r2 (construct-ranking (1 . 0))) 160 | (r3 (construct-ranking (1 . 0) (2 . 1)))) 161 | (check-equal? (rf->assoc (failure)) `()) 162 | (check-equal? (rf->assoc (construct-ranking (1 . 0))) `((1 . 0))) 163 | (check-equal? (rf->assoc (construct-ranking (1 . 0) (2 . 1))) `((1 . 0) (2 . 1))) 164 | (check-equal? (rf->assoc (construct-ranking (1 . 0) (2 . 1) (2 . 2))) `((1 . 0) (2 . 1))))) 165 | 166 | (test-case 167 | "set-global-dedup" 168 | (let ((with_dedup (begin (set-global-dedup #T) (rf->assoc (nrm/exc 10 10 10)))) 169 | (without-dedup (begin (set-global-dedup #F) (let ((ret (rf->assoc (nrm/exc 10 10 10)))) (begin (set-global-dedup #T) ret))))) 170 | (check-equal? with_dedup `((10 . 0))) 171 | (check-equal? without-dedup `((10 . 0) (10 . 10)))))) 172 | 173 | -------------------------------------------------------------------------------- /rp-core.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require racket/match) 4 | (provide (all-defined-out)) 5 | (require srfi/1) 6 | 7 | ; return value captured (as value-promise) in given ranking element 8 | (define value (lambda (x) (force (car x)))) 9 | 10 | ; return value-promise stored in given ranking element 11 | (define value-promise car) 12 | 13 | ; return rank stored in given ranking element 14 | (define rank cadr) 15 | 16 | ; return successor element-promise stored in given ranking element 17 | (define successor-promise caddr) 18 | 19 | ; return successor ranking element stored (as element-promise) in given ranking element 20 | (define successor (lambda (x) (force (caddr x)))) 21 | 22 | ; Create new ranking element with given value-promise, rank and successor-promise 23 | (define (element value-promise rank successor-promise) 24 | (list value-promise rank successor-promise)) 25 | 26 | ; Return element-promise that captures the terminal ranking element 27 | (define terminate-promise 28 | (delay terminate-element)) 29 | 30 | ; The terminal element (marks end of ranking function chain) 31 | (define terminate-element 32 | (element (delay 0) +inf.0 terminate-promise)) 33 | 34 | ; map: create new ranking from given ranking by applying 35 | ; the given function to each value. The new ranking function 36 | ; contains the values returned by the function. 37 | (define (map-value f rf) 38 | (delay 39 | (let ((res (force rf))) 40 | (if (infinite? (rank res)) 41 | terminate-element 42 | (element 43 | (delay (f (value res))) 44 | (rank res) 45 | (map-value f (successor-promise res))))))) 46 | 47 | ; map-value-promise: like map-value, except that f takes 48 | ; a value promise as argument, and must return a value promise. 49 | (define (map-value-promise f rf) 50 | (delay 51 | (let ((res (force rf))) 52 | (if (infinite? (rank res)) 53 | terminate-element 54 | (element 55 | (f (value-promise res)) 56 | (rank res) 57 | (map-value-promise f (successor-promise res))))))) 58 | 59 | ; normalise ranking 60 | (define (normalise rf [s #f]) 61 | (delay 62 | (let ((res (force rf))) 63 | (if (infinite? (rank res)) 64 | terminate-element 65 | (element 66 | (value-promise res) 67 | (if s (- (rank res) s) 0) 68 | (normalise (successor-promise res) 69 | (if s s (rank res)))))))) 70 | 71 | ; shift rank 72 | (define (shift n rf) 73 | (delay 74 | (let ((res (force rf))) 75 | (if (infinite? (rank res)) 76 | terminate-element 77 | (element 78 | (value-promise res) 79 | (+ (rank res) n) 80 | (shift n (successor-promise res))))))) 81 | 82 | ; filter on condition 83 | (define (filter-ranking pred rf) 84 | (delay 85 | (let ((res (force rf))) 86 | (if (infinite? (rank res)) 87 | terminate-element 88 | (if (pred (value res)) 89 | (element 90 | (value-promise res) 91 | (rank res) 92 | (filter-ranking pred (successor-promise res))) 93 | (force (filter-ranking pred (successor-promise res)))))))) 94 | 95 | ; Only pass through first n elements 96 | (define (filter-after n rf) 97 | (if (= n 0) 98 | terminate-promise 99 | (delay 100 | (let ((res (force rf))) 101 | (if (infinite? (rank res)) 102 | terminate-element 103 | (element 104 | (value-promise res) 105 | (rank res) 106 | (filter-after (- n 1) (successor-promise res)))))))) 107 | 108 | ; Only pass through elements with rank less than or equal to r 109 | (define (up-to-rank r rf) 110 | (delay 111 | (let ((res (force rf))) 112 | (if (> (rank res) r) 113 | terminate-element 114 | (element 115 | (value-promise res) 116 | (rank res) 117 | (up-to-rank r (successor-promise res))))))) 118 | 119 | ; For each element, call f with element as argument 120 | (define (do-with rf f) 121 | (let ((res (force rf))) 122 | (when (not (infinite? (rank res))) 123 | (begin 124 | (f res) 125 | (do-with (successor-promise res) f))))) 126 | 127 | ; merge two ranking functions 128 | (define (merge rfa rfb [sr 0]) 129 | (delay 130 | (let ((resa (force rfa))) 131 | (if (= (rank resa) sr) 132 | (element 133 | (value-promise resa) 134 | (rank resa) 135 | (merge (successor-promise resa) rfb sr)) 136 | (let ((resb (force rfb))) 137 | (if (<= (rank resa) (rank resb)) 138 | (element 139 | (value-promise resa) 140 | (rank resa) 141 | (merge (successor-promise resa) (delay resb) (rank resa))) 142 | (element 143 | (value-promise resb) 144 | (rank resb) 145 | (merge (delay resa) (successor-promise resb) (rank resb))))))))) 146 | 147 | ; merge list of ranking functions 148 | (define (merge-list rf-list) 149 | (if (= (length rf-list) 1) 150 | (car rf-list) 151 | (merge 152 | (car rf-list) 153 | (merge-list (cdr rf-list))))) 154 | 155 | ; Join two ranking functions 156 | (define (join rfa rfb) 157 | (merge-apply rfa (λ (va) (map-value (λ (b) (list va b)) rfb)))) 158 | 159 | ; join list of ranking functions 160 | (define (join-list rf-list) 161 | (cond 162 | [(empty? rf-list) terminate-promise] 163 | [(= (length rf-list) 1) (map-value list (car rf-list))] 164 | [(= (length rf-list) 2) (join (first rf-list) (second rf-list))] 165 | [else (map-value 166 | (λ (x) (cons (first x) (second x))) 167 | (join (first rf-list) (join-list (cdr rf-list))))])) 168 | 169 | ; merge-apply: 170 | ; - rfs is a ranking function 171 | ; - f is function taking one argument and returning a ranking function 172 | ; what is returned is the merge of all ranking functions returned by f, 173 | ; for each value returned by rfs, where ranks are increased accordingly 174 | ;(define (merge-apply rfs f) 175 | ; (merge (shift (rank (force rfs)) (f (value (force rfs)))) 176 | ; (merge-apply (delay (successor (force rfs))) f))) 177 | (define (merge-apply rfs f) 178 | (delay 179 | (let ((res (force rfs))) 180 | (if (infinite? (rank res)) 181 | terminate-element 182 | (force 183 | (let ((res2 (successor res))) 184 | (merge-with-ranks 185 | (shift (rank res) (f (value res))) 186 | (rank res) 187 | (merge-apply (delay res2) f) 188 | (rank res2)))))))) 189 | 190 | ; merge with ranks known 191 | (define (merge-with-ranks rfa ra rfb rb) 192 | (cond 193 | [(and (infinite? ra) (infinite? rb)) terminate-promise] 194 | [(infinite? ra) rfb] 195 | [(infinite? rb) rfa] 196 | [(<= ra rb) 197 | (delay 198 | (let ((resa (force rfa))) 199 | (element 200 | (value-promise resa) 201 | (rank resa) 202 | (delay 203 | (let ((resa2 (successor resa))) 204 | (force (merge-with-ranks (delay resa2) (rank resa2) rfb rb)))))))] 205 | [else 206 | (delay 207 | (let ((resb (force rfb))) 208 | (element 209 | (value-promise resb) 210 | (rank resb) 211 | (delay 212 | (let ((resb2 (successor resb))) 213 | (force (merge-with-ranks rfa ra (delay resb2) (rank resb2))))))))])) 214 | 215 | ; check ranking correctness 216 | (define (check-correctness rf [c -1]) 217 | (delay 218 | (let ((res (force rf))) 219 | (if (and (infinite? (rank res)) (= c -1)) 220 | res 221 | (if (>= (rank res) c) 222 | (element (value-promise res) 223 | (rank res) 224 | (check-correctness (successor-promise res) (rank res))) 225 | (error "incorrect rank order" c (rank res))))))) 226 | 227 | ; deduplicate ranking (removes duplicate higher-ranked values) 228 | (define (dedup rf [s (set)]) 229 | (if (not global-dedup-enabled) 230 | rf 231 | (delay 232 | (let ((res (force rf))) 233 | (if (infinite? (rank res)) 234 | terminate-element 235 | (if (set-member? s (value res)) 236 | (force (dedup (successor-promise res) s)) 237 | (element (value-promise res) 238 | (rank res) 239 | (dedup (successor-promise res) (set-add s (value res)))))))))) 240 | 241 | ; convert ranking function to hash table (value->rank) 242 | (define (convert-to-hash rf) 243 | (letrec 244 | ((fill-hash 245 | (λ (rf table) 246 | (let ((res (force rf))) 247 | (when (not (infinite? (rank res))) 248 | (begin 249 | (when (not (hash-has-key? table (value res))) 250 | (hash-set! table (value res) (rank res))) 251 | (fill-hash (successor-promise res) table)))))) 252 | (tab (make-hash))) 253 | (fill-hash rf tab) tab)) 254 | 255 | ; convert ranking function to associative list ((value . rank) (value . rank) ...) 256 | (define (convert-to-assoc rf) 257 | (let ((res (force rf))) 258 | (if (infinite? (rank res)) 259 | `() 260 | (cons (cons (value res) (rank res)) (convert-to-assoc (successor-promise res)))))) 261 | 262 | ; convert ranking function to stream of pairs (value . rank) 263 | (define (convert-to-stream rf) 264 | (let ((res (force rf))) 265 | (if (infinite? (rank res)) 266 | empty-stream 267 | (stream-cons (cons (value res) (rank res)) (convert-to-stream (successor-promise res)))))) 268 | 269 | ; convert associative list ((value . rank) (value . rank) ...) to ranking funciton 270 | ; (rank order is not checked) 271 | (define (construct-from-assoc assoc-list) 272 | (delay 273 | (if (empty? assoc-list) 274 | terminate-element 275 | (element 276 | (delay (caar assoc-list)) 277 | (cdar assoc-list) 278 | (construct-from-assoc (cdr assoc-list)))))) 279 | 280 | ; Set the core deduplication setting: #T=enabled, #F=disabled (default=#T) 281 | (define global-dedup-enabled #T) 282 | (define (set-core-global-dedup x) (set! global-dedup-enabled x)) 283 | -------------------------------------------------------------------------------- /rp-api.rkt: -------------------------------------------------------------------------------- 1 | #lang racket 2 | 3 | (require racket/match) 4 | (require racket/format) 5 | (require srfi/1) 6 | (require "rp-core.rkt") 7 | 8 | (provide 9 | ranking? 10 | rank? 11 | ranking/c 12 | rank/c 13 | construct-ranking 14 | nrm/exc 15 | ! 16 | failure 17 | either-of 18 | either/or 19 | observe 20 | observe-r 21 | observe-e 22 | cut 23 | limit 24 | rank-of 25 | $ 26 | rlet 27 | rlet* 28 | rf-equal? 29 | rf->hash 30 | rf->assoc 31 | rf->stream 32 | pr-all 33 | pr-until 34 | pr-first 35 | pr 36 | set-global-dedup 37 | ) 38 | 39 | ; used as marker for ranking function (only for internal use) 40 | (define rf-marker `ranking-function) 41 | 42 | ; autocast: convert typed ranking function to ranking chain 43 | ; and any other value to ranking chain representing single-valued ranking function 44 | (define autocast 45 | (lambda (value) 46 | (if (ranking? value) 47 | (force (cdr value)) 48 | (element (delay value) 0 terminate-promise)))) 49 | 50 | ; is value a ranking function? 51 | (define (ranking? value) (and (pair? value) (eq? (car value) rf-marker))) 52 | 53 | ; ranking contract 54 | (define ranking/c 55 | (flat-named-contract 'ranking/c ranking?)) 56 | 57 | ; is value a rank? 58 | (define (rank? x) (or (exact-nonnegative-integer? x) (infinite? x))) 59 | 60 | ; rank contract 61 | (define rank/c 62 | (flat-named-contract 'rank/c rank?)) 63 | 64 | ; is value a one-argument function? () 65 | (define (one-arg-proc? proc) (and (procedure? proc) (procedure-arity-includes? proc 1))) 66 | 67 | ; convert given ranking function to typed ranking function 68 | (define (mark-as-rf rf) (cons rf-marker rf)) 69 | 70 | ; empty ranking 71 | (define terminate-rf (mark-as-rf terminate-promise)) 72 | 73 | ; failure (returns empty ranking) 74 | (define (failure) terminate-rf) 75 | 76 | ; construct ranking from associative pairs, e.g. (construct-ranking ("x" . 0) ("y" . 1)) 77 | (define-syntax construct-ranking 78 | (syntax-rules () 79 | ((construct-ranking a ...) 80 | (mark-as-rf 81 | (check-correctness 82 | (construct-from-assoc `(a ...))))))) 83 | 84 | ; truth 85 | (define ! 86 | (lambda (value) 87 | (mark-as-rf 88 | (element (delay value) 0 terminate-promise)))) 89 | 90 | ; nrm (alternative syntax) 91 | (define-syntax nrm/exc 92 | (syntax-rules (nrm exc) 93 | ((nrm/exc r-exp1 r-exp2) (nrm/exc r-exp1 r-exp2 1)) 94 | ((nrm/exc r-exp1 r-exp2 rank) 95 | (begin 96 | (unless (rank? rank) (raise-argument-error 'nrm/exc "rank (non-negative integer or infinity)" 1 r-exp1 rank r-exp2)) 97 | (mark-as-rf 98 | (dedup 99 | (normalise 100 | (merge 101 | (delay (autocast r-exp1)) 102 | (shift rank (delay (autocast r-exp2))))))))))) 103 | 104 | ; either-of 105 | (define (either-of lst) 106 | (unless (list? lst) (raise-argument-error 'either-of "list" 0 lst)) 107 | (letrec 108 | ((either-of* 109 | (λ (list) 110 | (if (empty? list) 111 | terminate-promise 112 | (delay (element (delay (car list)) 0 (either-of* (cdr list)))))))) 113 | (mark-as-rf (dedup (either-of* lst))))) 114 | 115 | ; either/or 116 | (define-syntax either/or 117 | (syntax-rules () 118 | ((either/or) (mark-as-rf terminate-promise)) 119 | ((either/or r-exp rest ...) (mark-as-rf (dedup (merge (delay (autocast r-exp)) (either* rest ...))))))) 120 | 121 | (define-syntax either* 122 | (syntax-rules () 123 | ((either*) terminate-promise) 124 | ((either* r-exp rest ...) (merge (delay (autocast r-exp)) (either* rest ...))))) 125 | 126 | ; observe 127 | (define (observe pred r) 128 | (begin 129 | (unless (one-arg-proc? pred) (raise-argument-error 'observe "predicate" 0 pred r)) 130 | (mark-as-rf (normalise (filter-ranking pred (delay (autocast r))))))) 131 | 132 | ; observe-r (result-oriented conditionalization, also called j-conditionalization) 133 | (define (observe-r x pred r) 134 | (unless (one-arg-proc? pred) (raise-argument-error 'observe-r "predicate" 0 pred x r)) 135 | (unless (rank? x) (raise-argument-error 'observe-r "rank (non-negative integer or infinity)" 1 pred x r)) 136 | (nrm/exc 137 | (observe pred r) 138 | (observe (compose not pred) r) x)) 139 | 140 | ; observe-e (evidence-oriented conditionalization, also called l-conditionalization) 141 | (define (observe-e x pred r) 142 | (unless (one-arg-proc? pred) (raise-argument-error 'observe-e "predicate" 0 pred x r)) 143 | (unless (rank? x) (raise-argument-error 'observe-e "rank (non-negative integer or infinity)" 1 pred x r)) 144 | (let* ((rp (rank-of pred r))) 145 | (if (< x rp) 146 | (observe-r (- rp x) pred r) 147 | (observe-r (+ (- x rp) (rank-of (compose not pred) r)) pred r)))) 148 | 149 | ; cut 150 | (define (cut rank r-exp) 151 | (unless (rank? rank) (raise-argument-error 'cut "rank (non-negative integer or infinity)" 0 rank r-exp)) 152 | (mark-as-rf (up-to-rank rank (delay (autocast r-exp))))) 153 | 154 | ; limit 155 | (define (limit count r-exp) 156 | (unless (or (exact-nonnegative-integer? count) (infinite? count)) (raise-argument-error 'cut "non-negative integer" 0 rank r-exp)) 157 | (mark-as-rf (filter-after count (delay (autocast r-exp))))) 158 | 159 | ; rank-of 160 | (define (rank-of pred r-exp) 161 | (unless (one-arg-proc? pred) (raise-argument-error 'rank-of "predicate" 0 pred r-exp)) 162 | (letrec ((rank-of* 163 | (λ (rf) 164 | (let* ((res (force rf))) 165 | (if (infinite? (rank res)) 166 | (rank res) 167 | (if (pred (value res)) 168 | (rank res) 169 | (rank-of* (successor-promise res)))))))) 170 | (rank-of* (delay (autocast r-exp))))) 171 | 172 | ; $ (ranked application) 173 | (define ($ . r-exps) 174 | (if (> (length r-exps) 0) 175 | (if (ranking? (car r-exps)) 176 | ; function argument is ranking over functions 177 | (mark-as-rf 178 | (dedup 179 | (merge-apply 180 | (map-value 181 | (λ (form) (delay (autocast (apply (car form) (cdr form))))) 182 | (dedup (join-list (map (λ (x) (delay (autocast x))) r-exps)))) 183 | (λ (rf) rf)))) 184 | (if (primitive? (car r-exps)) 185 | ; function argument is primitive (function will not return ranking) 186 | (mark-as-rf 187 | (dedup 188 | (map-value 189 | (λ (args) (apply (car r-exps) args)) 190 | (dedup (join-list (map (λ (x) (delay (autocast x))) (cdr r-exps))))))) 191 | ; function argument is not primitive (function may return ranking) 192 | (mark-as-rf 193 | (dedup 194 | (merge-apply 195 | (map-value 196 | (λ (args) (delay (autocast (apply (car r-exps) args)))) 197 | (dedup (join-list (map (λ (x) (delay (autocast x))) (cdr r-exps))))) 198 | (λ (rf) rf)))))) 199 | (raise-arity-error '$ (arity-at-least 1)))) 200 | 201 | ; ranked let 202 | (define-syntax rlet 203 | (syntax-rules () 204 | ((rlet ((var r-exp) ...) body ...) 205 | ($ (λ (var ...) body ...) r-exp ...)))) 206 | 207 | ; ranked let* 208 | (define-syntax rlet* 209 | (syntax-rules () 210 | ((rlet* () body) body) ; base case 211 | ((rlet* ((var r-exp) rest ...) body) ; binding case 212 | ($ (λ (var) (rlet* (rest ...) body)) r-exp)))) 213 | 214 | ; returns true if two ranking functions are equal (disregards ordering of values with equal rank and redundant elements) 215 | (define (rf-equal? r-exp1 r-exp2) 216 | (equal? (rf->hash r-exp1) (rf->hash r-exp2))) 217 | 218 | ; rf->hash (convert ranking to hash table value->rank) 219 | (define (rf->hash r-exp) 220 | (convert-to-hash (delay (autocast r-exp)))) 221 | 222 | ; rf->assoc (convert ranking to associative list of (value . rank) pairs) 223 | (define (rf->assoc r-exp) 224 | (convert-to-assoc (dedup (delay (autocast r-exp))))) 225 | 226 | ; convert ranking function to stream of (value . rank) pairs 227 | (define (rf->stream r-exp) 228 | (convert-to-stream (dedup (delay (autocast r-exp))))) 229 | 230 | ; Display helper functions 231 | (define display-header (λ () (begin (display "Rank Value") (newline) (display "------------") (newline)))) 232 | (define display-failure (λ () (begin (display "Failure (empty ranking)") (newline)))) 233 | (define display-done (λ () (begin (display "Done") (newline)))) 234 | (define display-more (λ () (begin (display "...") (newline)))) 235 | (define display-element (λ (el) (begin (display (~a (rank el) #:min-width 5)) (display " ") (display (value el)) (newline)))) 236 | 237 | ; Print complete ranking 238 | (define (pr-all r-exp) 239 | (unless (void? r-exp) 240 | (let ((first-res (force (autocast r-exp)))) 241 | (if (infinite? (rank first-res)) 242 | (display-failure) 243 | (begin (display-header) (do-with (delay first-res) display-element) (display-done)))))) 244 | 245 | ; Print ranking for given r-expression up to given rank 246 | (define (pr-until r r-exp) 247 | (unless (rank? r) (raise-argument-error 'observe-j "rank (non-negative integer or infinity)" 0 rank r-exp)) 248 | (unless (void? r-exp) 249 | (let ((first-res (force (autocast r-exp)))) 250 | (if (infinite? (rank first-res)) 251 | (display-failure) 252 | (begin (display-header) (pr-until* r first-res)))))) 253 | 254 | (define (pr-until* r res) 255 | (if (infinite? (rank res)) 256 | (display-done) 257 | (if (> (rank res) r) 258 | (display-more) 259 | (begin (display-element res) (pr-until* r (successor res)))))) 260 | 261 | ; Print first n lowest-ranked values of ranking for given r-expression 262 | (define (pr-first n r-exp) 263 | (unless (void? r-exp) 264 | (let ((first-res (force (autocast r-exp)))) 265 | (if (infinite? (rank first-res)) 266 | (display-failure) 267 | (begin (display-header) (pr-first* n first-res)))))) 268 | 269 | (define (pr-first* n res) 270 | (if (infinite? (rank res)) 271 | (display-done) 272 | (begin (display-element res) 273 | (if (= n 1) (display-more) (pr-first* (- n 1) (successor res)))))) 274 | 275 | ; Print first 10 lowest-ranked values of ranking for given r-expression 276 | (define (pr r-exp) (pr-first 10 r-exp)) 277 | 278 | ; Change the global dedup setting (0 = no deduplication, any other value = full deduplication) 279 | (define (set-global-dedup x) (set-core-global-dedup x)) 280 | 281 | 282 | -------------------------------------------------------------------------------- /scribblings/scribble.css: -------------------------------------------------------------------------------- 1 | 2 | /* This file is used by default by all Scribble documents. 3 | See also "manual.css", which is added by default by the 4 | `scribble/manual` language. */ 5 | 6 | /* CSS seems backward: List all the classes for which we want a 7 | particular font, so that the font can be changed in one place. (It 8 | would be nicer to reference a font definition from all the places 9 | that we want it.) 10 | 11 | As you read the rest of the file, remember to double-check here to 12 | see if any font is set. */ 13 | 14 | /* Monospace: */ 15 | .maincolumn, .refpara, .refelem, .tocset, .stt, .hspace, .refparaleft, .refelemleft { 16 | font-family: monospace; 17 | } 18 | 19 | /* Serif: */ 20 | .main, .refcontent, .tocview, .tocsub, .sroman, i { 21 | font-family: serif; 22 | } 23 | 24 | /* Sans-serif: */ 25 | .version, .versionNoNav, .ssansserif { 26 | font-family: sans-serif; 27 | } 28 | .ssansserif { 29 | font-size: 80%; 30 | font-weight: bold; 31 | } 32 | 33 | /* ---------------------------------------- */ 34 | 35 | p, .SIntrapara { 36 | display: block; 37 | margin: 1em 0; 38 | } 39 | 40 | h2 { /* per-page main title */ 41 | margin-top: 0; 42 | } 43 | 44 | h3, h4, h5, h6, h7, h8 { 45 | margin-top: 1.75em; 46 | margin-bottom: 0.5em; 47 | } 48 | 49 | .SSubSubSubSection { 50 | font-weight: bold; 51 | font-size: 0.83em; /* should match h5; from HTML 4 reference */ 52 | } 53 | 54 | /* Needed for browsers like Opera, and eventually for HTML 4 conformance. 55 | This means that multiple paragraphs in a table element do not have a space 56 | between them. */ 57 | table p { 58 | margin-top: 0; 59 | margin-bottom: 0; 60 | } 61 | 62 | /* ---------------------------------------- */ 63 | /* Main */ 64 | 65 | body { 66 | color: black; 67 | background-color: #ffffff; 68 | } 69 | 70 | table td { 71 | padding-left: 0; 72 | padding-right: 0; 73 | } 74 | 75 | .maincolumn { 76 | width: 43em; 77 | margin-right: -40em; 78 | margin-left: 15em; 79 | } 80 | 81 | .main { 82 | text-align: left; 83 | } 84 | 85 | /* ---------------------------------------- */ 86 | /* Navigation */ 87 | 88 | .navsettop, .navsetbottom { 89 | background-color: #f0f0e0; 90 | padding: 0.25em 0 0.25em 0; 91 | } 92 | 93 | .navsettop { 94 | margin-bottom: 1.5em; 95 | border-bottom: 2px solid #e0e0c0; 96 | } 97 | 98 | .navsetbottom { 99 | margin-top: 2em; 100 | border-top: 2px solid #e0e0c0; 101 | } 102 | 103 | .navleft { 104 | margin-left: 1ex; 105 | position: relative; 106 | float: left; 107 | white-space: nowrap; 108 | } 109 | .navright { 110 | margin-right: 1ex; 111 | position: relative; 112 | float: right; 113 | white-space: nowrap; 114 | } 115 | .nonavigation { 116 | color: #e0e0e0; 117 | } 118 | 119 | .searchform { 120 | display: inline; 121 | margin: 0; 122 | padding: 0; 123 | } 124 | 125 | .nosearchform { 126 | display: none; 127 | } 128 | 129 | .searchbox { 130 | width: 16em; 131 | margin: 0px; 132 | padding: 0px; 133 | background-color: #eee; 134 | border: 1px solid #ddd; 135 | text-align: center; 136 | vertical-align: middle; 137 | } 138 | 139 | #contextindicator { 140 | position: fixed; 141 | background-color: #c6f; 142 | color: #000; 143 | font-family: monospace; 144 | font-weight: bold; 145 | padding: 2px 10px; 146 | display: none; 147 | right: 0; 148 | bottom: 0; 149 | } 150 | 151 | /* ---------------------------------------- */ 152 | /* Version */ 153 | 154 | .versionbox { 155 | position: relative; 156 | float: right; 157 | left: 2em; 158 | height: 0em; 159 | width: 13em; 160 | margin: 0em -13em 0em 0em; 161 | } 162 | .version { 163 | font-size: small; 164 | } 165 | .versionNoNav { 166 | font-size: xx-small; /* avoid overlap with author */ 167 | } 168 | 169 | .version:before, .versionNoNav:before { 170 | content: "Version "; 171 | } 172 | 173 | /* ---------------------------------------- */ 174 | /* Margin notes */ 175 | 176 | .refpara, .refelem { 177 | position: relative; 178 | float: right; 179 | left: 2em; 180 | height: 0em; 181 | width: 13em; 182 | margin: 0em -13em 0em 0em; 183 | } 184 | 185 | .refpara, .refparaleft { 186 | top: -1em; 187 | } 188 | 189 | .refcolumn { 190 | background-color: #F5F5DC; 191 | display: block; 192 | position: relative; 193 | width: 13em; 194 | font-size: 85%; 195 | border: 0.5em solid #F5F5DC; 196 | margin: 0 0 0 0; 197 | white-space: normal; /* in case margin note is inside code sample */ 198 | } 199 | 200 | .refcontent { 201 | margin: 0 0 0 0; 202 | } 203 | 204 | .refcontent p { 205 | margin-top: 0; 206 | margin-bottom: 0; 207 | } 208 | 209 | .refparaleft, .refelemleft { 210 | position: relative; 211 | float: left; 212 | right: 2em; 213 | height: 0em; 214 | width: 13em; 215 | margin: 0em 0em 0em -13em; 216 | } 217 | 218 | .refcolumnleft { 219 | background-color: #F5F5DC; 220 | display: block; 221 | position: relative; 222 | width: 13em; 223 | font-size: 85%; 224 | border: 0.5em solid #F5F5DC; 225 | margin: 0 0 0 0; 226 | } 227 | 228 | 229 | /* ---------------------------------------- */ 230 | /* Table of contents, inline */ 231 | 232 | .toclink { 233 | text-decoration: none; 234 | color: blue; 235 | font-size: 85%; 236 | } 237 | 238 | .toptoclink { 239 | text-decoration: none; 240 | color: blue; 241 | font-weight: bold; 242 | } 243 | 244 | /* ---------------------------------------- */ 245 | /* Table of contents, left margin */ 246 | 247 | .tocset { 248 | position: relative; 249 | float: left; 250 | width: 12.5em; 251 | margin-right: 2em; 252 | } 253 | .tocset td { 254 | vertical-align: text-top; 255 | } 256 | 257 | .tocview { 258 | text-align: left; 259 | background-color: #f0f0e0; 260 | } 261 | 262 | .tocsub { 263 | text-align: left; 264 | margin-top: 0.5em; 265 | background-color: #f0f0e0; 266 | } 267 | 268 | .tocviewlist, .tocsublist { 269 | margin-left: 0.2em; 270 | margin-right: 0.2em; 271 | padding-top: 0.2em; 272 | padding-bottom: 0.2em; 273 | } 274 | .tocviewlist table { 275 | font-size: 82%; 276 | } 277 | 278 | .tocviewlisttopspace { 279 | margin-bottom: 1em; 280 | } 281 | 282 | .tocviewsublist, .tocviewsublistonly, .tocviewsublisttop, .tocviewsublistbottom { 283 | margin-left: 0.4em; 284 | border-left: 1px solid #bbf; 285 | padding-left: 0.8em; 286 | } 287 | .tocviewsublist { 288 | margin-bottom: 1em; 289 | } 290 | .tocviewsublist table, 291 | .tocviewsublistonly table, 292 | .tocviewsublisttop table, 293 | .tocviewsublistbottom table { 294 | font-size: 75%; 295 | } 296 | 297 | .tocviewtitle * { 298 | font-weight: bold; 299 | } 300 | 301 | .tocviewlink { 302 | text-decoration: none; 303 | color: blue; 304 | } 305 | 306 | .tocviewselflink { 307 | text-decoration: underline; 308 | color: blue; 309 | } 310 | 311 | .tocviewtoggle { 312 | text-decoration: none; 313 | color: blue; 314 | font-size: 75%; /* looks better, and avoids bounce when toggling sub-sections due to font alignments */ 315 | } 316 | 317 | .tocsublist td { 318 | padding-left: 1em; 319 | text-indent: -1em; 320 | } 321 | 322 | .tocsublinknumber { 323 | font-size: 82%; 324 | } 325 | 326 | .tocsublink { 327 | font-size: 82%; 328 | text-decoration: none; 329 | } 330 | 331 | .tocsubseclink { 332 | font-size: 82%; 333 | text-decoration: none; 334 | } 335 | 336 | .tocsubnonseclink { 337 | font-size: 82%; 338 | text-decoration: none; 339 | padding-left: 0.5em; 340 | } 341 | 342 | .tocsubtitle { 343 | font-size: 82%; 344 | font-style: italic; 345 | margin: 0.2em; 346 | } 347 | 348 | /* ---------------------------------------- */ 349 | /* Some inline styles */ 350 | 351 | .indexlink { 352 | text-decoration: none; 353 | } 354 | 355 | .nobreak { 356 | white-space: nowrap; 357 | } 358 | 359 | pre { margin-left: 2em; } 360 | blockquote { margin-left: 2em; } 361 | 362 | ol { list-style-type: decimal; } 363 | ol ol { list-style-type: lower-alpha; } 364 | ol ol ol { list-style-type: lower-roman; } 365 | ol ol ol ol { list-style-type: upper-alpha; } 366 | 367 | .SCodeFlow { 368 | display: block; 369 | margin-left: 1em; 370 | margin-bottom: 0em; 371 | margin-right: 1em; 372 | margin-top: 0em; 373 | white-space: nowrap; 374 | } 375 | 376 | .SVInsetFlow { 377 | display: block; 378 | margin-left: 0em; 379 | margin-bottom: 0em; 380 | margin-right: 0em; 381 | margin-top: 0em; 382 | } 383 | 384 | .SubFlow { 385 | display: block; 386 | margin: 0em; 387 | } 388 | 389 | .boxed { 390 | width: 100%; 391 | background-color: #E8E8FF; 392 | } 393 | 394 | .hspace { 395 | } 396 | 397 | .slant { 398 | font-style: oblique; 399 | } 400 | 401 | .badlink { 402 | text-decoration: underline; 403 | color: red; 404 | } 405 | 406 | .plainlink { 407 | text-decoration: none; 408 | color: blue; 409 | } 410 | 411 | .techoutside { text-decoration: underline; color: #b0b0b0; } 412 | .techoutside:hover { text-decoration: underline; color: blue; } 413 | 414 | /* .techinside:hover doesn't work with FF, .techinside:hover> 415 | .techinside doesn't work with IE, so use both (and IE doesn't 416 | work with inherit in the second one, so use blue directly) */ 417 | .techinside { color: black; } 418 | .techinside:hover { color: blue; } 419 | .techoutside:hover>.techinside { color: inherit; } 420 | 421 | .SCentered { 422 | text-align: center; 423 | } 424 | 425 | .imageleft { 426 | float: left; 427 | margin-right: 0.3em; 428 | } 429 | 430 | .Smaller { 431 | font-size: 82%; 432 | } 433 | 434 | .Larger { 435 | font-size: 122%; 436 | } 437 | 438 | /* A hack, inserted to break some Scheme ids: */ 439 | .mywbr { 440 | display: inline-block; 441 | height: 0; 442 | width: 0; 443 | font-size: 1px; 444 | } 445 | 446 | .compact li p { 447 | margin: 0em; 448 | padding: 0em; 449 | } 450 | 451 | .noborder img { 452 | border: 0; 453 | } 454 | 455 | .SVerbatim { 456 | white-space: nowrap; 457 | } 458 | 459 | .SAuthorListBox { 460 | position: relative; 461 | float: right; 462 | left: 2em; 463 | top: -2.5em; 464 | height: 0em; 465 | width: 13em; 466 | margin: 0em -13em 0em 0em; 467 | } 468 | .SAuthorList { 469 | font-size: 82%; 470 | } 471 | .SAuthorList:before { 472 | content: "by "; 473 | } 474 | .author { 475 | display: inline; 476 | white-space: nowrap; 477 | } 478 | 479 | /* print styles : hide the navigation elements */ 480 | @media print { 481 | .tocset, 482 | .navsettop, 483 | .navsetbottom { display: none; } 484 | .maincolumn { 485 | width: auto; 486 | margin-right: 13em; 487 | margin-left: 0; 488 | } 489 | } 490 | -------------------------------------------------------------------------------- /scribblings/manual-style.css: -------------------------------------------------------------------------------- 1 | 2 | /* See the beginning of "scribble.css". 3 | This file is used by the `scribble/manual` language, along with 4 | "manual-racket.css". */ 5 | 6 | @import url("manual-fonts.css"); 7 | 8 | * { 9 | margin: 0; 10 | padding: 0; 11 | } 12 | 13 | @media all {html {font-size: 15px;}} 14 | @media all and (max-width:940px){html {font-size: 14px;}} 15 | @media all and (max-width:850px){html {font-size: 13px;}} 16 | @media all and (max-width:830px){html {font-size: 12px;}} 17 | @media all and (max-width:740px){html {font-size: 11px;}} 18 | 19 | /* CSS seems backward: List all the classes for which we want a 20 | particular font, so that the font can be changed in one place. (It 21 | would be nicer to reference a font definition from all the places 22 | that we want it.) 23 | 24 | As you read the rest of the file, remember to double-check here to 25 | see if any font is set. */ 26 | 27 | /* Monospace: */ 28 | .maincolumn, .refpara, .refelem, .tocset, .stt, .hspace, .refparaleft, .refelemleft { 29 | font-family: 'Fira-Mono', monospace; 30 | white-space: inherit; 31 | font-size: 1rem; 32 | } 33 | 34 | /* embolden the "Racket Guide" and "Racket Reference" links on the TOC */ 35 | /* there isn't an obvious tag in the markup that designates the top TOC page, which is called "start.scrbl" */ 36 | /* nor a tag that designates these two links as special */ 37 | /* so we'll use this slightly tortured sibling selector that hooks onto the h2 tag */ 38 | h2[x-source-module='(lib "scribblings/main/start.scrbl")'] ~ table a[href="guide/index.html"], 39 | h2[x-source-module='(lib "scribblings/main/start.scrbl")'] ~ table a[href="reference/index.html"] { 40 | font-weight: bold; 41 | } 42 | 43 | 44 | h2 .stt { 45 | font-size: 2.3rem; 46 | /* prevent automatic bolding from h2 */ 47 | font-weight: 400; 48 | } 49 | 50 | .toptoclink .stt { 51 | font-size: inherit; 52 | } 53 | .toclink .stt { 54 | font-size: 90%; 55 | } 56 | 57 | .RpackageSpec .stt { 58 | font-weight: 300; 59 | font-family: 'Fira-Mono', monospace; 60 | font-size: 0.9rem; 61 | } 62 | 63 | h3 .stt, h4 .stt, h5 .stt { 64 | color: #333; 65 | font-size: 1.65rem; 66 | font-weight: 400; 67 | } 68 | 69 | 70 | /* Serif: */ 71 | .main, .refcontent, .tocview, .tocsub, .sroman, i { 72 | font-family: 'Charter-Racket', serif; 73 | font-size: 1.18rem; 74 | /* Don't use font-feature-settings with Charter, 75 | it fouls up loading for reasons mysterious */ 76 | /* font-feature-settings: 'tnum' 1, 'liga' 0; */ 77 | } 78 | 79 | 80 | /* Sans-serif: */ 81 | .version, .versionNoNav, .ssansserif { 82 | font-family: 'Fira', sans-serif; 83 | } 84 | 85 | /* used mostly for DrRacket menu commands */ 86 | .ssansserif { 87 | font-family: 'Fira', sans-serif; 88 | font-size: 0.9em; 89 | } 90 | 91 | .tocset .ssansserif { 92 | font-size: 100%; 93 | } 94 | 95 | /* ---------------------------------------- */ 96 | 97 | p, .SIntrapara { 98 | display: block; 99 | margin: 0 0 1em 0; 100 | line-height: 1.4; 101 | } 102 | 103 | .compact { 104 | padding: 0 0 1em 0; 105 | } 106 | 107 | li { 108 | list-style-position: outside; 109 | margin-left: 1.2em; 110 | } 111 | 112 | h1, h2, h3, h4, h5, h6, h7, h8 { 113 | font-family: 'Fira', sans-serif; 114 | font-weight: 300; 115 | font-size: 1.6rem; 116 | color: #333; 117 | margin-top: inherit; 118 | margin-bottom: 1rem; 119 | line-height: 1.25; 120 | 121 | } 122 | 123 | h3, h4, h5, h6, h7, h8 { 124 | border-top: 1px solid black; 125 | } 126 | 127 | 128 | 129 | h2 { /* per-page main title */ 130 | font-family: 'Cooper-Hewitt'; 131 | margin-top: 4rem; 132 | font-size: 2.3rem; 133 | font-weight: bold; 134 | line-height: 1.2; 135 | width: 90%; 136 | /* a little nudge to make text visually lower than 4rem rule in left margin */ 137 | position: relative; 138 | top: 6px; 139 | } 140 | 141 | h3, h4, h5, h6, h7, h8 { 142 | margin-top: 2em; 143 | padding-top: 0.1em; 144 | margin-bottom: 0.75em; 145 | } 146 | 147 | /* ---------------------------------------- */ 148 | /* Main */ 149 | 150 | body { 151 | color: black; 152 | background-color: white; 153 | } 154 | 155 | .maincolumn { 156 | width: auto; 157 | margin-top: 4rem; 158 | margin-left: 17rem; 159 | margin-right: 2rem; 160 | margin-bottom: 10rem; /* to avoid fixed bottom nav bar */ 161 | max-width: 700px; 162 | min-width: 370px; /* below this size, code samples don't fit */ 163 | } 164 | 165 | a { 166 | text-decoration: inherit; 167 | } 168 | 169 | a, .toclink, .toptoclink, .tocviewlink, .tocviewselflink, .tocviewtoggle, .plainlink, 170 | .techinside, .techoutside:hover, .techinside:hover { 171 | color: #07A; 172 | } 173 | 174 | a:hover { 175 | text-decoration: underline; 176 | } 177 | 178 | 179 | /* ---------------------------------------- */ 180 | /* Navigation */ 181 | 182 | .navsettop, .navsetbottom { 183 | left: 0; 184 | width: 15rem; 185 | height: 6rem; 186 | font-family: 'Fira', sans-serif; 187 | font-size: 0.9rem; 188 | border-bottom: 0px solid hsl(216, 15%, 70%); 189 | background-color: inherit; 190 | padding: 0; 191 | } 192 | 193 | .navsettop { 194 | position: absolute; 195 | top: 0; 196 | left: 0; 197 | margin-bottom: 0; 198 | border-bottom: 0; 199 | } 200 | 201 | .navsettop a, .navsetbottom a { 202 | color: black; 203 | } 204 | 205 | .navsettop a:hover, .navsetbottom a:hover { 206 | background: hsl(216, 78%, 95%); 207 | text-decoration: none; 208 | } 209 | 210 | .navleft, .navright { 211 | position: static; 212 | float: none; 213 | margin: 0; 214 | white-space: normal; 215 | } 216 | 217 | 218 | .navleft a { 219 | display: inline-block; 220 | } 221 | 222 | .navright a { 223 | display: inline-block; 224 | text-align: center; 225 | } 226 | 227 | .navleft a, .navright a, .navright span { 228 | display: inline-block; 229 | padding: 0.5rem; 230 | min-width: 1rem; 231 | } 232 | 233 | 234 | .navright { 235 | height: 2rem; 236 | white-space: nowrap; 237 | } 238 | 239 | 240 | .navsetbottom { 241 | display: none; 242 | } 243 | 244 | .nonavigation { 245 | color: #889; 246 | } 247 | 248 | .searchform { 249 | display: block; 250 | margin: 0; 251 | padding: 0; 252 | border-bottom: 1px solid #eee; 253 | height: 4rem; 254 | } 255 | 256 | .nosearchform { 257 | margin: 0; 258 | padding: 0; 259 | height: 4rem; 260 | } 261 | 262 | .searchbox { 263 | font-size: 0.9rem; 264 | width: 12rem; 265 | margin: 1rem; 266 | padding: 0.25rem 0.4rem ; 267 | vertical-align: middle; 268 | background-color: white; 269 | font-family: 'Fira-Mono', monospace; 270 | } 271 | 272 | 273 | #search_box { 274 | font-family: 'Fira-Mono', monospace; 275 | font-size: 1rem; 276 | padding: 0.25rem 0.3rem ; 277 | } 278 | 279 | /* Default to local view. Global will specialize */ 280 | .plt_global_only { display: none; } 281 | .plt_local_only { display: block; } 282 | 283 | /* ---------------------------------------- */ 284 | /* Version */ 285 | 286 | .versionbox { 287 | position: absolute; 288 | float: none; 289 | top: 0.25rem; 290 | left: 17rem; 291 | z-index: 11000; 292 | height: 2em; 293 | font-size: 70%; 294 | font-weight: lighter; 295 | width: inherit; 296 | margin: 0; 297 | } 298 | .version, .versionNoNav { 299 | font-size: inherit; 300 | } 301 | .version:before, .versionNoNav:before { 302 | content: "v."; 303 | } 304 | 305 | 306 | /* ---------------------------------------- */ 307 | /* Margin notes */ 308 | 309 | /* cancel scribble.css styles: */ 310 | .refpara, .refelem { 311 | position: static; 312 | float: none; 313 | height: auto; 314 | width: auto; 315 | margin: 0; 316 | } 317 | 318 | .refcolumn { 319 | position: static; 320 | display: block; 321 | width: auto; 322 | font-size: inherit; 323 | margin: 2rem; 324 | margin-left: 2rem; 325 | padding: 0.5em; 326 | padding-left: 0.75em; 327 | padding-right: 1em; 328 | background: hsl(60, 29%, 94%); 329 | border: 1px solid #ccb; 330 | border-left: 0.4rem solid #ccb; 331 | } 332 | 333 | 334 | /* slightly different handling for margin-note* on narrow screens */ 335 | @media all and (max-width:1340px) { 336 | span.refcolumn { 337 | float: right; 338 | width: 50%; 339 | margin-left: 1rem; 340 | margin-bottom: 0.8rem; 341 | margin-top: 1.2rem; 342 | } 343 | 344 | } 345 | 346 | .refcontent, .refcontent p { 347 | line-height: 1.5; 348 | margin: 0; 349 | } 350 | 351 | .refcontent p + p { 352 | margin-top: 1em; 353 | } 354 | 355 | .refcontent a { 356 | font-weight: 400; 357 | } 358 | 359 | .refpara, .refparaleft { 360 | top: -1em; 361 | } 362 | 363 | 364 | @media all and (max-width:600px) { 365 | .refcolumn { 366 | margin-left: 0; 367 | margin-right: 0; 368 | } 369 | } 370 | 371 | 372 | @media all and (min-width:1340px) { 373 | .refcolumn { 374 | margin: 0 -22.5rem 1rem 0; 375 | float: right; 376 | clear: right; 377 | width: 18rem; 378 | } 379 | } 380 | 381 | .refcontent { 382 | font-family: 'Fira', sans-serif; 383 | font-size: 1rem; 384 | line-height: 1.6; 385 | margin: 0 0 0 0; 386 | } 387 | 388 | 389 | .refparaleft, .refelemleft { 390 | position: relative; 391 | float: left; 392 | right: 2em; 393 | height: 0em; 394 | width: 13em; 395 | margin: 0em 0em 0em -13em; 396 | } 397 | 398 | .refcolumnleft { 399 | background-color: hsl(60, 29%, 94%); 400 | display: block; 401 | position: relative; 402 | width: 13em; 403 | font-size: 85%; 404 | border: 0.5em solid hsl(60, 29%, 94%); 405 | margin: 0 0 0 0; 406 | } 407 | 408 | 409 | /* ---------------------------------------- */ 410 | /* Table of contents, left margin */ 411 | 412 | .tocset { 413 | position: absolute; 414 | float: none; 415 | left: 0; 416 | top: 0rem; 417 | width: 14rem; 418 | padding: 7rem 0.5rem 0.5rem 0.5rem; 419 | background-color: hsl(216, 15%, 70%); 420 | margin: 0; 421 | 422 | } 423 | 424 | .tocset td { 425 | vertical-align: text-top; 426 | padding-bottom: 0.4rem; 427 | padding-left: 0.2rem; 428 | line-height: 1.1; 429 | font-family: 'Fira', sans-serif; 430 | } 431 | 432 | .tocset td a { 433 | color: black; 434 | font-weight: 400; 435 | } 436 | 437 | 438 | .tocview { 439 | text-align: left; 440 | background-color: inherit; 441 | } 442 | 443 | 444 | .tocview td, .tocsub td { 445 | line-height: 1.3; 446 | } 447 | 448 | 449 | .tocview table, .tocsub table { 450 | width: 90%; 451 | } 452 | 453 | .tocset td a.tocviewselflink { 454 | font-weight: lighter; 455 | font-size: 110%; /* monospaced styles below don't need to enlarge */ 456 | color: white; 457 | } 458 | 459 | .tocviewselflink { 460 | text-decoration: none; 461 | } 462 | 463 | .tocsub { 464 | text-align: left; 465 | margin-top: 0.5em; 466 | background-color: inherit; 467 | } 468 | 469 | .tocviewlist, .tocsublist { 470 | margin-left: 0.2em; 471 | margin-right: 0.2em; 472 | padding-top: 0.2em; 473 | padding-bottom: 0.2em; 474 | } 475 | .tocviewlist table { 476 | font-size: 82%; 477 | } 478 | 479 | .tocviewlisttopspace { 480 | margin-bottom: 1em; 481 | } 482 | 483 | .tocviewsublist, .tocviewsublistonly, .tocviewsublisttop, .tocviewsublistbottom { 484 | margin-left: 0.4em; 485 | border-left: 1px solid #99a; 486 | padding-left: 0.8em; 487 | } 488 | .tocviewsublist { 489 | margin-bottom: 1em; 490 | } 491 | .tocviewsublist table, 492 | .tocviewsublistonly table, 493 | .tocviewsublisttop table, 494 | .tocviewsublistbottom table, 495 | table.tocsublist { 496 | font-size: 1rem; 497 | } 498 | 499 | .tocviewsublist td, 500 | .tocviewsublistbottom td, 501 | .tocviewsublisttop td, 502 | .tocsub td, 503 | .tocviewsublistonly td { 504 | font-size: 90%; 505 | } 506 | 507 | /* shrink the monospaced text (`stt`) within nav */ 508 | .tocviewsublist td .stt, 509 | .tocviewsublistbottom td .stt, 510 | .tocviewsublisttop td .stt, 511 | .tocsub td .stt, 512 | .tocviewsublistonly td .stt { 513 | font-size: 95%; 514 | } 515 | 516 | 517 | .tocviewtoggle { 518 | font-size: 75%; /* looks better, and avoids bounce when toggling sub-sections due to font alignments */ 519 | } 520 | 521 | .tocsublist td { 522 | padding-left: 0.5rem; 523 | padding-top: 0.25rem; 524 | text-indent: 0; 525 | } 526 | 527 | .tocsublinknumber { 528 | font-size: 100%; 529 | } 530 | 531 | .tocsublink { 532 | font-size: 82%; 533 | text-decoration: none; 534 | } 535 | 536 | .tocsubseclink { 537 | font-size: 100%; 538 | text-decoration: none; 539 | } 540 | 541 | .tocsubnonseclink { 542 | font-size: 82%; 543 | text-decoration: none; 544 | margin-left: 1rem; 545 | padding-left: 0; 546 | display: inline-block; 547 | } 548 | 549 | /* the label "on this page" */ 550 | .tocsubtitle { 551 | display: block; 552 | font-size: 62%; 553 | font-family: 'Fira', sans-serif; 554 | font-weight: bolder; 555 | font-style: normal; 556 | letter-spacing: 2px; 557 | text-transform: uppercase; 558 | margin: 0.5em; 559 | } 560 | 561 | .toptoclink { 562 | font-weight: bold; 563 | font-size: 110%; 564 | margin-bottom: 0.5rem; 565 | margin-top: 1.5rem; 566 | display: inline-block; 567 | } 568 | 569 | .toclink { 570 | font-size: inherit; 571 | } 572 | 573 | /* ---------------------------------------- */ 574 | /* Some inline styles */ 575 | 576 | .indexlink { 577 | text-decoration: none; 578 | } 579 | 580 | pre { 581 | margin-left: 2em; 582 | } 583 | 584 | blockquote { 585 | margin-left: 2em; 586 | margin-right: 2em; 587 | margin-bottom: 1em; 588 | } 589 | 590 | .SCodeFlow { 591 | border-left: 1px dotted black; 592 | padding-left: 1em; 593 | padding-right: 1em; 594 | margin-top: 1em; 595 | margin-bottom: 1em; 596 | margin-left: 0em; 597 | margin-right: 2em; 598 | white-space: nowrap; 599 | line-height: 1.5; 600 | } 601 | 602 | .SCodeFlow img { 603 | margin-top: 0.5em; 604 | margin-bottom: 0.5em; 605 | } 606 | 607 | /* put a little air between lines of code sample */ 608 | /* Fira Mono appears taller than Source Code Pro */ 609 | .SCodeFlow td { 610 | padding-bottom: 1px; 611 | } 612 | 613 | .boxed { 614 | margin: 0; 615 | margin-top: 2em; 616 | padding: 0.25em; 617 | padding-top: 0.3em; 618 | padding-bottom: 0.4em; 619 | background: #f3f3f3; 620 | box-sizing:border-box; 621 | border-top: 1px solid #99b; 622 | background: hsl(216, 78%, 95%); 623 | background: -moz-linear-gradient(to bottom left, hsl(0, 0%, 99%) 0%, hsl(216, 62%, 95%) 100%); 624 | background: -webkit-linear-gradient(to bottom left, hsl(0, 0%, 99%) 0%, hsl(216, 62%, 95%) 100%); 625 | background: -o-linear-gradient(to bottom left, hsl(0, 0%, 99%) 0%, hsl(216, 62%, 95%) 100%); 626 | background: -ms-linear-gradient(to bottom left, hsl(0, 0%, 99%) 0%, hsl(216, 62%, 95%) 100%); 627 | background: linear-gradient(to bottom left, hsl(0, 0%, 99%) 0%, hsl(216, 62%, 95%) 100%); 628 | } 629 | 630 | blockquote > blockquote.SVInsetFlow { 631 | /* resolves issue in e.g. /reference/notation.html */ 632 | margin-top: 0em; 633 | } 634 | 635 | .leftindent .SVInsetFlow { /* see e.g. section 4.5 of Racket Guide */ 636 | margin-top: 1em; 637 | margin-bottom: 1em; 638 | } 639 | 640 | .SVInsetFlow a, .SCodeFlow a { 641 | color: #07A; 642 | } 643 | 644 | .SubFlow { 645 | display: block; 646 | margin: 0em; 647 | } 648 | 649 | .boxed { 650 | width: 100%; 651 | background-color: inherit; 652 | } 653 | 654 | .techoutside { text-decoration: none; } 655 | 656 | .SAuthorListBox { 657 | position: static; 658 | float: none; 659 | font-family: 'Fira', sans-serif; 660 | font-weight: 300; 661 | font-size: 110%; 662 | margin-top: 1rem; 663 | margin-bottom: 2rem; 664 | width: 30rem; 665 | height: auto; 666 | } 667 | 668 | .author > a { /* email links within author block */ 669 | font-weight: inherit; 670 | color: inherit; 671 | } 672 | 673 | .SAuthorList { 674 | font-size: 82%; 675 | } 676 | .SAuthorList:before { 677 | content: "by "; 678 | } 679 | .author { 680 | display: inline; 681 | white-space: nowrap; 682 | } 683 | 684 | /* phone + tablet styles */ 685 | 686 | @media all and (max-width:720px){ 687 | 688 | 689 | @media all and (max-width:720px){ 690 | 691 | @media all {html {font-size: 15px;}} 692 | @media all and (max-width:700px){html {font-size: 14px;}} 693 | @media all and (max-width:630px){html {font-size: 13px;}} 694 | @media all and (max-width:610px){html {font-size: 12px;}} 695 | @media all and (max-width:550px){html {font-size: 11px;}} 696 | @media all and (max-width:520px){html {font-size: 10px;}} 697 | 698 | .navsettop, .navsetbottom { 699 | display: block; 700 | position: absolute; 701 | width: 100%; 702 | height: 4rem; 703 | border: 0; 704 | background-color: hsl(216, 15%, 70%); 705 | } 706 | 707 | .searchform { 708 | display: inline; 709 | border: 0; 710 | } 711 | 712 | .navright { 713 | position: absolute; 714 | right: 1.5rem; 715 | margin-top: 1rem; 716 | border: 0px solid red; 717 | } 718 | 719 | .navsetbottom { 720 | display: block; 721 | margin-top: 8rem; 722 | } 723 | 724 | .tocset { 725 | display: none; 726 | } 727 | 728 | .tocset table, .tocset tbody, .tocset tr, .tocset td { 729 | display: inline; 730 | } 731 | 732 | .tocview { 733 | display: none; 734 | } 735 | 736 | .tocsub .tocsubtitle { 737 | display: none; 738 | } 739 | 740 | .versionbox { 741 | top: 4.5rem; 742 | left: 1rem; /* same distance as main-column */ 743 | z-index: 11000; 744 | height: 2em; 745 | font-size: 70%; 746 | font-weight: lighter; 747 | } 748 | 749 | 750 | .maincolumn { 751 | margin-left: 1em; 752 | margin-top: 7rem; 753 | margin-bottom: 0rem; 754 | } 755 | 756 | } 757 | 758 | } 759 | 760 | /* print styles : hide the navigation elements */ 761 | @media print { 762 | .tocset, 763 | .navsettop, 764 | .navsetbottom { display: none; } 765 | .maincolumn { 766 | width: auto; 767 | margin-right: 13em; 768 | margin-left: 0; 769 | } 770 | } 771 | -------------------------------------------------------------------------------- /scribblings/ranked-programming.scrbl: -------------------------------------------------------------------------------- 1 | #lang scribble/manual 2 | @(require racket/sandbox 3 | scribble/example) 4 | 5 | @require[@for-label[ranked-programming 6 | racket/base]] 7 | @(require (for-label racket/base 8 | racket/contract/base 9 | racket/struct-info 10 | ranked-programming 11 | racket/stream) 12 | ;scribble/extract 13 | ;scribble-math 14 | ;latex-utils/scribble/math 15 | ranked-programming) 16 | 17 | 18 | @title{Ranked Programming} 19 | @author{Tjitze Rienstra} 20 | 21 | @defmodule[ranked-programming] 22 | 23 | @section{Introduction} 24 | 25 | The @racket[ranked-programming] package implements ranked programming functionality for the Racket programming language. 26 | For background and general introduction on ranked programming please read 27 | @(let ([url "https://github.com/tjitze/ranked-programming/blob/master/documentation/ranked_programming.pdf"])(link url "this paper")) 28 | (presented at IJCAI 2019). 29 | 30 | This document contains a complete reference of the functionality provided by this package. 31 | A quick-start guide for this package can be found @(let ([url "https://github.com/tjitze/ranked-programming/blob/master/README.md"]) (link url "here")). 32 | 33 | Before using this reference, the reader should be familiar with the paper linked to above. 34 | There are a few minor differences between the language described in the paper and the language implemented here, 35 | as well as a number of additional features not discussed in the paper. 36 | We list the differences and additions here: 37 | 38 | @bold{Ranked Choice} 39 | 40 | The syntax of the @italic{ranked choice} expression discussed in the paper is 41 | 42 | @verbatim{(nrm K1 exc R K2)} 43 | 44 | The ranked choice expression implemented by this library uses a different syntax: 45 | 46 | @racket[(nrm/exc K1 K2 R)]. 47 | 48 | @bold{Either/Or} 49 | 50 | The syntax of the @italic{either/or} expression discussed in the paper is 51 | 52 | @verbatim{(either K1 or K2)} 53 | 54 | The either/or expression implemented by this library uses a different syntax: 55 | 56 | @racket[(either/or K1 K2)]. 57 | 58 | @bold{Truth expressions} 59 | 60 | The @italic{truth expression} @racket[!x] described in the paper is implemented by the procedure @racket[!]. 61 | This means that we must enclose these expressions in parantheses. 62 | Thus, instead of writing 63 | 64 | @verbatim{(nrm/exc !"foo" !"bar" 1)} 65 | 66 | like in the paper, we have to write 67 | 68 | @racket[(nrm/exc (! "foo") (! "bar") 1)] 69 | 70 | @bold{However}, all expressions with parameters of type ranking are implemented 71 | so that these parameters also accept values of any other type. 72 | Such values are implicitly converted to rankings using @racket[!]. 73 | Therefore, the @racket[!] procedure is actually redundant, because instead of @racket[(nrm/exc (! "foo") (! "bar") 1)] we can simply write 74 | 75 | @racket[(nrm/exc "foo" "bar" 1)] 76 | 77 | where @racket["foo"] and @racket["bar"] are implicitly converted to @racket[(! "foo")] and @racket[(! "bar")], 78 | since they appear as arguments to parameters of type ranking. 79 | 80 | @bold{Displaying Ranking Functions} 81 | 82 | In this text, ranking functions returned by expressions are referred to simply as @italic{rankings}. 83 | They encode sets of possible return values of an expression, associated with degrees of surprise: 84 | 0 for not surprising, 1 for surprising, 2 for even more surprising, and so on. 85 | These rankings are represented by lazily-linked list data structures, as discussed in section 4 in the 86 | @(let ([url "https://github.com/tjitze/ranked-programming/blob/master/documentation/ranked_programming.pdf"]) 87 | (link url "paper")). 88 | In order to display a ranking, we need to provide it as an argument to one of the print functions implemented by this library. 89 | The standard print function is @racket[pr]. 90 | Thus, instead of evaluating an expression like @racket[(nrm/exc "foo" "bar" 1)] directly, we must evaluate it as follows. 91 | 92 | @examples[ #:label #f #:eval ((make-eval-factory #:lang 'racket/base 93 | '(ranked-programming))) 94 | (pr (nrm/exc "foo" "bar" 1)) 95 | ] 96 | 97 | Alternatives to @racket[pr] are @racket[pr-all], @racket[pr-until] and @racket[pr-first] (see reference for details). 98 | 99 | @bold{Doing other things with rankings} 100 | 101 | Apart from displaying a ranking, we can also convert it to some other, more manageable, representation. 102 | For this, we can use the @racket[rf->hash], @racket[rf->assoc] and @racket[rf->stream] functions, 103 | which convert a ranking to, respectively, a hash table, an association list, or a stream. 104 | The @racket[cut] and @racket[limit] procedures may also be of use in combination with these functions. 105 | 106 | @examples[ #:label "Example:" #:eval ((make-eval-factory #:lang 'racket/base 107 | '(ranked-programming))) 108 | (rf->assoc (nrm/exc "foo" "bar")) 109 | ] 110 | 111 | @bold{Additional functions and expression types} 112 | 113 | This library implements all functions and expression types discussed in the paper. 114 | These are the 115 | truth function (@racket[!]), 116 | ranked choice expression (@racket[nrm/exc]), 117 | the either/or syntactic shortcut (@racket[either/or]), 118 | observation function (@racket[observe]), 119 | ranked procedure call function (@racket[$]), 120 | and ranked @racket[let*] expression (@racket[rlet*]). 121 | 122 | This library also provides the following additional functions and expression types, which are not described in the paper: 123 | 124 | @itemlist[ 125 | @item{@racket[either-of] Choose elements from a list (all equally surprising).} 126 | @item{@racket[construct-ranking] Construct ranking from an association list.} 127 | @item{@racket[rank-of] Return rank of a predicate according to a given ranking.} 128 | @item{@racket[failure] Returns the empty ranking.} 129 | @item{@racket[rlet] Generalises @racket[let], like @racket[rlet*] generalises @racket[let*].} 130 | @item{@racket[rf-equal?] Check if two rankings are equivalent.} 131 | @item{@racket[rf->hash]/@racket[rf->assoc]/@racket[rf->stream] Convert ranking to other data structure.} 132 | @item{@racket[pr-all]/@racket[pr-first]/@racket[pr-until]/@racket[pr] Procedures for displaying a ranking.} 133 | @item{@racket[observe-r]/@racket[observe-e] Generalised @racket[observe] variants.} 134 | @item{@racket[cut] Restrict ranking up to a given rank.} 135 | @item{@racket[limit] Restrict ranking up to a given number of values.} 136 | @item{@racket[rank?]/@racket[ranking?] Type checking for ranks and rankings.} 137 | @item{@racket[rank/c]/@racket[ranking/c] Type contracts for ranks and rankings.} 138 | ] 139 | 140 | These are all described in detail in this reference. 141 | 142 | @section{Reference} 143 | 144 | @defform*[((nrm/exc k_1 k_2 rank) (nrm/exc k_1 k_2)) 145 | #:contracts ([k_1 (any/c)] [k_2 (any/c)] [rank (rank?)])]{ 146 | 147 | @italic{Normally} returns the value captured by @racket[k_1] and @italic{exceptionally} (with degree of surprise @racket[rank]) 148 | the value captured by @racket[k_2]. 149 | If @racket[rank] is omitted, it defaults to 1. 150 | 151 | @examples[ #:eval ((make-eval-factory #:lang 'racket/base 152 | '(ranked-programming))) 153 | (pr (nrm/exc "foo" "bar")) 154 | (pr (nrm/exc "foo" "bar" 2)) 155 | ] 156 | 157 | If @racket[k_1] and @racket[k_2] are rankings then @racket[(nrm/exc k_1 k_2 rank)] returns a ranking 158 | according to which the rank of a value @racket[v] is the minimum among 159 | the rank of @racket[v] according to the ranking @racket[k_1], 160 | and the rank of @racket[v] according to the ranking @racket[k_2] @bold{plus the value of} @racket[rank]. 161 | 162 | @examples[ #:label #f #:eval ((make-eval-factory #:lang 'racket/base 163 | '(ranked-programming))) 164 | (pr (nrm/exc "foo" (nrm/exc "bar" "baz"))) 165 | ] 166 | 167 | Both @racket[k_1] and @racket[k_2] are evaluated on an as-needed basis. This means that 168 | @racket[k_1] is evaluated only after the ranking that is returned is consulted for the first time, 169 | and @racket[k_2] only after it is consulted beyond rank @racket[rank]. 170 | This @italic{lazy evaluation} scheme avoids needless calculations and provides the ability 171 | to define potentially infinite rankings. 172 | 173 | Below is an example of an infinite ranking. 174 | The expression @racket[(recur x)] normally returns @racket[x] and exceptionally @racket[(recur (* x 2))]. 175 | Even though @racket[recur] is infinitely recursive, it does return a ranking, which is due to the lazy evaluation. 176 | 177 | @examples[ #:label #f #:eval ((make-eval-factory #:lang 'racket/base 178 | '(ranked-programming))) 179 | (define (recur x) (nrm/exc x (recur (* x 2)))) 180 | (pr (recur 1)) 181 | ] 182 | } 183 | 184 | @defform[(either/or k_1 ... k_n)]{ 185 | 186 | Returns a ranking according to which @racket[k_1 ... k_n] all equally surprising. 187 | 188 | @examples[ #:label "Example:" #:eval ((make-eval-factory #:lang 'racket/base 189 | '(ranked-programming))) 190 | (pr (nrm/exc "peter" (either/or "ann" "bob" "charlie"))) 191 | ] 192 | 193 | If @racket[k_1 ... k_n] are rankings, then @racket[(either/or k_1 ... k_n)] returns a ranking 194 | according to which the rank of a value @racket[v] is the minimum among 195 | the ranks of @racket[v] according to the rankings @racket[k_1 ... k_n]. 196 | 197 | @examples[ #:eval ((make-eval-factory #:lang 'racket/base 198 | '(ranked-programming))) 199 | (pr (either/or (nrm/exc "peter" "ann") (nrm/exc "bob" "charly"))) 200 | ] 201 | } 202 | 203 | @defproc[(either-of [lst (list?)]) 204 | ranking?]{ 205 | 206 | Returns a ranking according to which all elements of the list @racket[lst] are equally surprising. 207 | 208 | @examples[ #:label "Example:" #:eval ((make-eval-factory #:lang 'racket/base 209 | '(ranked-programming))) 210 | (define weekdays (list "mon" "tue" "wed" "thu" "fri")) 211 | (define weekend (list "sat" "sun")) 212 | (pr (nrm/exc (either-of weekdays) (either-of weekend))) 213 | ] 214 | } 215 | 216 | @defform[(! v)]{ 217 | 218 | Constructs a ranking according to which @racket[v] is ranked 0 and anything else ranked infinity. 219 | 220 | @examples[ #:eval ((make-eval-factory #:lang 'racket/base 221 | '(ranked-programming))) 222 | (pr (! 5)) 223 | ] 224 | 225 | This function (called the @italic{Truth function} in the paper) is included for the sake of completeness 226 | but is actually redundant. This is because all expressions provided by this library with parameters of 227 | type ranking are implemented so that these parameters also accept values of any other type. Such values 228 | are implicitly converted to rankings using @racket[!]. See discussion in the introduction.} 229 | 230 | @defform[(construct-ranking (v_1 . r_1) ... (v_n . r_n))]{ 231 | 232 | Constructs a ranking from an association list. 233 | The values @racket[v_1] ... @racket[v_n] are returned with ranks @racket[r_1] ... @racket[r_n]. 234 | Rank @racket[r_1] must be 0, and @racket[r_1] ... @racket[r_n] must be sorted in non-decreasing order. 235 | 236 | @examples[ #:eval ((make-eval-factory #:lang 'racket/base 237 | '(ranked-programming))) 238 | (pr (construct-ranking ("x" . 0) ("y" . 1) ("z" . 5))) 239 | ] 240 | } 241 | 242 | @defproc[(rank-of [pred (any/c -> boolean?)] [k (ranking/c)]) rank?]{ 243 | 244 | Returns the rank of the predicate @racket[pred] according to the ranking @racket[k]. 245 | This value represents the degree of surprise that @racket[pred] holds according to @racket[k]. 246 | It is the rank of the lowest-ranked value for which @racket[pred] returns @racket[#t]. 247 | 248 | The following example determines the degree of surprise that @racket[(recur 1)] returns a value higher than 500. 249 | 250 | @examples[ #:label #f #:eval ((make-eval-factory #:lang 'racket/base 251 | '(ranked-programming))) 252 | (define (recur x) (nrm/exc x (recur (* x 2)) 1)) 253 | (rank-of (lambda (x) (> x 500)) (recur 1)) 254 | ] 255 | 256 | The ranking @racket[k] is consulted as much as necessary but no more. 257 | More precisely, @racket[k] is consulted until a value for which @racket[pred] returns @racket[#t] is encountered. 258 | 259 | If @racket[pred] does not return @racket[#t] for some finitely-ranked value, 260 | and @racket[k] is infinite (i.e., assigns finite ranks to infinitely many values) 261 | then @racket[rank-of] does not terminate. 262 | 263 | The predicate @racket[pred] is only called for values that have a finite rank according to @racket[k]. 264 | If @racket[pred] does not terminate for one of these values then @racket[rank-of] might also not terminate. 265 | If @racket[pred] throws an error for one of these values then @racket[rank-of] might also throw an error.} 266 | 267 | @defproc[(failure) ranking?]{ 268 | Returns an empty ranking. 269 | } 270 | 271 | @defproc[(observe [pred (any/c -> boolean?)] [k (ranking?)]) 272 | ranking?]{ 273 | Returns the ranking @racket[k] conditionalized on the predicate @racket[pred]. 274 | This is the ranking-theoretic conditionalization operation, 275 | which is the ranking-based analogue of the probabilistic contitionalization operation. 276 | 277 | The ranking returned by the expression @racket[(observe pred k)] is determined by the following rule: 278 | Suppose @racket[k] assigns a finite rank @racket[r] to the value @racket[v]. Then: 279 | @itemlist[ 280 | @item{if @racket[(pred v)] returns @racket[#f] then @racket[v] is discarded (or returned with rank infinity).} 281 | @item{if @racket[(pred v)] returns @racket[#t] then @racket[v] is returned with rank @racket[r] minus @racket[(rank-of pred k)].}] 282 | 283 | In the following example we determine the ranking returned by @racket[(recur 1)] given that 284 | we observe that the value that is returned is higher than 500. 285 | 286 | @examples[ #:eval ((make-eval-factory #:lang 'racket/base 287 | '(ranked-programming))) 288 | (define (recur x) (nrm/exc x (recur (* x 2)) 1)) 289 | (pr (observe (lambda (x) (> x 500)) (recur 1))) 290 | ] 291 | 292 | The predicate @racket[pred] is only called for values that have a finite rank according to @racket[k]. 293 | If @racket[pred] does not terminate for one of these values then @racket[observe] might also not terminate. 294 | If @racket[pred] throws an error for one of these values then @racket[observe] might also throw an error.} 295 | } 296 | 297 | @defproc[($ [k_1 any/c] ... [k_n any/c]) 298 | ranking?]{ 299 | 300 | Returns the result of applying the procedure @racket[k_1] to the arguments @racket[k_2 ... k_n]. 301 | 302 | The precise rule that is used to construct the ranking returned by the expression @racket[($ k_1 ... k_n)] is as follows: 303 | Suppose that the rankings @racket[k_1 ... k_n] assign ranks @racket[r_1 ... r_n] to the values @racket[v_1 ... v_n]. 304 | Furthermore suppose that the standard procedure call @racket[(v_1 ... v_n)] returns @racket[v]. 305 | Then @racket[v] is returned with rank @racket[r_1]+...+@racket[r_n], 306 | unless there is another sequence of values that yields a lower rank for @racket[v] using the same rule. 307 | 308 | Consider the procedure call @racket[(+ 5 10)]. 309 | The ranked version of this is @racket[($ + 5 10)]: 310 | 311 | @examples[ #:label #f #:eval ((make-eval-factory #:lang 'racket/base 312 | '(ranked-programming))) 313 | (pr ($ + 5 10)) 314 | ] 315 | 316 | Now suppose we are uncertain about the argument @racket[10]: 317 | this value is normally @racket[10] and exceptionally @racket[20]. 318 | To express this we replace @racket[10] with @racket[(nrm/exc 10 20)]: 319 | 320 | @examples[ #:label #f #:eval ((make-eval-factory #:lang 'racket/base 321 | '(ranked-programming))) 322 | (pr ($ + 5 (nrm/exc 10 20))) 323 | ] 324 | 325 | Now we add uncertainty about the operation: we normally add but exceptionally multiply: 326 | 327 | @examples[ #:label #f #:eval ((make-eval-factory #:lang 'racket/base 328 | '(ranked-programming))) 329 | (pr ($ (nrm/exc + *) 5 (nrm/exc 10 20))) 330 | ] 331 | } 332 | 333 | @defform[(rlet ([var_1 k_1] ... [var_n k_n]) body)]{ 334 | 335 | The @racket[rlet] expression generalises Racket's standard @racket[let] expression. 336 | Like in the standard @racket[let] expression, @racket[var_1 ... var_n] are variables, 337 | and @racket[body] is an expression in which these variables may occur. 338 | The difference with the standard @racket[let] expression is that the @racket[k_1 ... k_n] parameters expect arguments of type ranking. 339 | 340 | The precise rule that is used to construct the ranking returned by the expression @racket[(rlet ([var_1 k_1] ... [var_n k_n]) body)] is as follows: 341 | Suppose that the rankings @racket[k_1 ... k_n] assign ranks @racket[r_1 ... r_n] to the values @racket[v_1 ... v_n]. 342 | Furthermore, suppose that @racket[body], with occurrences of @racket[var_1 ... var_n] replaced with @racket[v_1 ... v_n], returns @racket[v]. 343 | Then @racket[v] is returned with rank @racket[r_1]+...+@racket[r_n], 344 | unless there is another sequence of values that yields a lower rank for @racket[v] using the same rule. 345 | 346 | The @racket[rlet] expression provides a convenient way to construct a joint ranking over a set of independent variables. 347 | An example: let @racket[b] and @racket[p] be boolean variables standing for beer and peanuts. 348 | We only exceptionally drink beer, and thus @racket[b] becomes @racket[(nrm/exc #f #t)]. 349 | Furthermore, we normally eat peanuts, and thus @racket[b] becomes @racket[(nrm/exc #t #f)]. 350 | 351 | @examples[ #:label #f #:eval ((make-eval-factory #:lang 'racket/base 352 | '(ranked-programming))) 353 | (pr (rlet 354 | ((b (nrm/exc #f #t)) 355 | (p (nrm/exc #t #f))) 356 | (string-append (if b "beer" "no beer") " and " 357 | (if p "peanuts" "no peanuts")))) 358 | ] 359 | } 360 | 361 | One may wish to express dependencies between variables. 362 | In the example above, we might wish to express that our peanut consumption depends on whether we drink beer. 363 | However, this cannot be done, since the argument for @racket[p] cannot refer to the value of @racket[b]. 364 | The @racket[rlet*] expression extends the @racket[rlet] expression and provides a solution for such cases. 365 | 366 | @defform[(rlet* ([var_1 k_1] ... [var_n k_n]) body)]{ 367 | 368 | The @racket[rlet*] expression generalises Racket's standard @racket[let*] expression 369 | in a way similar to how @racket[rlet] generalises @racket[let]. 370 | 371 | The rule used to determine the ranking that is returned is the same as that of @racket[rlet], 372 | except that the expressions used as arguments for @racket[k_1 ... k_n] may refer to the preceding variables. 373 | This provides a convenient way to construct a joint ranking over a list of variables, 374 | where each variable may depend on the preceding variables. 375 | 376 | An example: let @racket[b] and @racket[p] be boolean variables standing for "beer" and "peanuts". 377 | Like before, we only exceptionally drink beer, and thus @racket[b] becomes @racket[(nrm/exc #f #t)]. 378 | However, this time our peanut consumption depends on whether we drink beer: 379 | if we do, we normally have peanuts, and otherwise we don't. 380 | Thus, @racket[p] becomes @racket[(if b (nrm/exc #t #f) #f)]. 381 | Note that this expression refers to @racket[b], 382 | which would not be allowed if we used @racket[rlet] instead of @racket[rlet*]. 383 | 384 | @examples[ #:label #f #:eval ((make-eval-factory #:lang 'racket/base 385 | '(ranked-programming))) 386 | (pr (rlet* 387 | ((b (nrm/exc #f #t)) 388 | (p (if b (nrm/exc #t #f) #f))) 389 | (string-append (if b "beer" "no beer") " and " 390 | (if p "peanuts" "no peanuts")))) 391 | ] 392 | } 393 | 394 | @defproc[(rf-equal? [k_1 (ranking/c)] [k_2 (ranking/c)]) boolean?]{ 395 | Returns @racket[#t] if @racket[k_1] and @racket[k_2] are equivalent rankings, and @racket[#f] otherwise. 396 | Two rankings are equivalent if they assign the same rank to each finitely-ranked value. 397 | This means that the order in which values with the same rank are returned is irrelevant. 398 | This procedure will not terminate if @racket[k_1] and @racket[k_2] are infinite rankings 399 | (i.e., assign finite ranks to infinitely many values). 400 | 401 | } 402 | 403 | @defproc[(rf->hash [k (ranking/c)]) hash?]{ 404 | Converts the ranking @racket[k] to a 405 | @(let ([url "https://docs.racket-lang.org/guide/hash-tables.html"]) 406 | (link url "hash table")) that maps each finitely ranked value to its rank. 407 | This procedure will not terminate if @racket[k] is an infinite ranking 408 | (i.e., assigns finite ranks to infinitely many values). 409 | } 410 | 411 | @defproc[(rf->assoc [k (ranking/c)]) list?]{ 412 | Converts the ranking @racket[k] to an association list, 413 | which is a list consisting of pairs @racket[(v . r)] 414 | for each finitely ranked value @racket[v] and rank @racket[r]. 415 | These pairs appear in non-decreasing order with respect to rank. 416 | If @racket[k] is an infinite ranking (i.e., assigns finite ranks to infinitely many values) this function will not terminate. 417 | } 418 | 419 | @defproc[(rf->stream [k (ranking/c)]) stream?]{ 420 | Converts the ranking @racket[k] to a stream that generates pairs @racket[(value . rank)] 421 | for each finitely ranked value and its rank (see @racket[racket/stream]). 422 | These pairs are generated in non-decreasing order with respect to rank. 423 | The ranking @racket[k] will be consulted one value at a time, as the stream is consumed. 424 | If @racket[k] is an infinite ranking (i.e., assigns finite ranks to infinitely many values) then this function returns an infinite stream. 425 | } 426 | 427 | @defproc[(pr-all [k (ranking/c)]) void?]{ 428 | Displays the complete ranking @racket[k] in tabular form and in non-decreasing order with respect to rank. 429 | If @racket[k] is an infinite ranking (i.e., assigns finite ranks to infinitely many values) this function will not terminate. 430 | } 431 | 432 | @defproc[(pr-first [n (natural-number/c)] [k (ranking/c)]) void?]{ 433 | Like @racket[pr-all] but only displays the @racket[n] lowest-ranked values. 434 | } 435 | 436 | @defproc[(pr-until [rank (natural-number/c)] [k (ranking/c)]) void?]{ 437 | Like @racket[pr-all] but only displays values up to rank @racket[rank]. 438 | } 439 | 440 | @defproc[(pr [k (ranking/c)]) void?]{ 441 | Displays the 10 lowest-ranked values of the ranking @racket[k]. 442 | Short for @racket[(pr-first 10 k)]. 443 | } 444 | 445 | @defproc[(observe-r [x (rank?)] [pred (any/c -> boolean?)] [k (ranking?)]) ranking?]{ 446 | Like @racket[observe] but implements the more general @italic{result-oriented} conditionalization operation, 447 | where @racket[x] is the extra posterior belief strength parameter.} 448 | 449 | @defproc[(observe-e [x (rank?)] [pred (any/c -> boolean?)] [k (ranking?)]) ranking?]{ 450 | Like @racket[observe] but implements the more general @italic{evidence-oriented} conditionalization operation, 451 | where @racket[x] is the extra evidence strength parameter.} 452 | 453 | @defproc[(cut [rank (rank?)] [k (ranking?)]) ranking?]{ 454 | Returns the ranking @racket[k] restricted to values with a rank of at most @racket[rank]. 455 | } 456 | 457 | @defproc[(limit [count (rank?)] [k (ranking?)]) ranking?]{ 458 | Returns the ranking @racket[k] restricted to the @racket[count] lowest-ranked values. 459 | } 460 | 461 | @defproc[(set-global-dedup [enabled (boolean?)]) void?]{ 462 | Sets the global deduplication setting. If enabled, duplicate higher-ranked values are filtered 463 | out of any ranking that is computed. If disabled, duplicates may occur (e.g. a ranking where some 464 | value x occurs more than once, where the higher-ranked occurrence is redundant). By default, this 465 | setting is enabled. Disabling it will lead to less memory consumption, since duplicate detection 466 | requires memorisation of values. In some cases, disabling deduplication can speed up inference. In 467 | other cases, disabling deduplication slows down inference, due to redundant computations. Which is 468 | better depends on the implementation. 469 | 470 | Without deduplication: 471 | @examples[ #:label #f #:eval ((make-eval-factory #:lang 'racket/base 472 | '(ranked-programming))) 473 | (set-global-dedup #F) 474 | (pr (nrm/exc "a" "a" 5)) 475 | ] 476 | 477 | With deduplication: 478 | @examples[ #:label #f #:eval ((make-eval-factory #:lang 'racket/base 479 | '(ranked-programming))) 480 | (set-global-dedup #T) 481 | (pr (nrm/exc "a" "a" 5)) 482 | ]} 483 | 484 | @defproc[(rank? [x (any/c)]) boolean?]{ 485 | Returns @racket[#t] if @racket[x] is a rank (a non-negative integer or infinity) and @racket[#f] otherwise. 486 | } 487 | 488 | @defproc[(ranking? [x (any/c)]) boolean?]{ 489 | Returns @racket[#t] if @racket[x] is a ranking and @racket[#f] otherwise. 490 | } 491 | 492 | @defthing[rank/c flat-contract?]{ 493 | A contract for ranks (non-negative integers or infinity). 494 | } 495 | 496 | @defthing[ranking/c flat-contract?]{ 497 | A contract for rankings. 498 | } 499 | 500 | --------------------------------------------------------------------------------