├── 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 |
--------------------------------------------------------------------------------