• R/O
  • HTTP
  • SSH
  • HTTPS

提交

标签
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

A categorical programming language


Commit MetaInfo

修订版245f670944ef60b609a95029f37db32924d75ba4 (tree)
时间2022-10-17 02:57:01
作者Corbin <cds@corb...>
CommiterCorbin

Log Message

Rewrite the trampoline in Purescript.

Took a little bit of effort, but it seems worthwhile! The generated code
has the desired loop! I'm impressed.

更改概述

差异

--- a/.envrc
+++ b/.envrc
@@ -1,4 +1,4 @@
1-# export NIX_PATH=nixpkgs=/home/simpson/nixpkgs
1+export NIX_PATH=nixpkgs=/home/simpson/nixpkgs
22 use nix
33 eval $(keychain --eval --agents ssh)
44
--- /dev/null
+++ b/honey/cammy.purs
@@ -0,0 +1,41 @@
1+module Cammy where
2+
3+data Unit = Unit
4+type Lazy = (->) Unit
5+data Free f t = Pure t | Free (f (Free f t))
6+
7+-- Cribbed Bilby trampoline, modified and extended to give a monad.
8+-- Improved using https://stackoverflow.com/a/64854259/264985
9+-- Further improved by ripping off Control.Monad.Trampoline
10+type Tramp = Free Lazy
11+
12+done :: forall t. t -> Tramp t
13+done = Pure
14+
15+cont :: forall t. (Unit -> Tramp t) -> Tramp t
16+cont = Free
17+
18+fmap :: forall s t. (s -> t) -> Tramp s -> Tramp t
19+fmap f (Pure x) = Pure (f x)
20+fmap f (Free thunk) = Free (\u -> fmap f (thunk u))
21+
22+bind :: forall s t. (s -> Tramp t) -> Tramp s -> Tramp t
23+bind f (Pure x) = f x
24+bind f (Free thunk) = Free (\u -> bind f (thunk u))
25+
26+join :: forall t. Tramp (Tramp t) -> Tramp t
27+join = bind id
28+
29+runTrampoline :: forall t. Tramp t -> t
30+runTrampoline (Pure x) = x
31+runTrampoline (Free thunk) = runTrampoline (thunk Unit)
32+
33+lift :: forall s t. (s -> t) -> s -> Tramp t
34+lift f x = Pure (f x)
35+
36+id :: forall t. t -> t
37+id x = x
38+
39+comp :: forall r s t. (r -> Tramp s) -> (s -> Tramp t) -> r -> Tramp t
40+comp f g x = bind g (f x)
41+
--- a/honey/honey.py
+++ b/honey/honey.py
@@ -9,6 +9,7 @@ from .constants import CORE_PRIMITIVES, CORE_TEMPLATES
99 from .parser import parse
1010
1111 MOVELIST = "/nix/store/dhlswr6d9057lvdq7394dgmlncviv7za-movelist/bin/"
12+PURESCRIPT = "/nix/store/sf7hbhk4bbfq9px1y6442pvlmd6z2zn5-purescript-0.14.7/bin/"
1213
1314 app = Flask(__name__)
1415
@@ -78,7 +79,7 @@ def home():
7879 </div>
7980 <script
8081 src="{ url_for("static", filename="honey.js") }"
81- type="text/javascript"></script>
82+ type="module"></script>
8283 <datalist id="dippers">
8384 {dippers}
8485 </datalist>
--- a/honey/static/honey.js
+++ b/honey/static/honey.js
@@ -1,3 +1,5 @@
1+import * as Cammy from "./Cammy/index.js";
2+
13 function fetchJSON(url, options = {}) {
24 return fetch(url, options).then(resp => resp.json());
35 }
@@ -35,51 +37,9 @@ class Extractor {
3537 }
3638 }
3739
38-function rescueNaN(f) {
39- return x => {
40- let y = f(x);
41- return isNaN(y) ? [false, null] : [true, y];
42- };
43-}
44-
45-// Cribbed Bilby trampoline, modified and extended to give a monad.
46-// Improved using https://stackoverflow.com/a/64854259/264985
47-// X -> Tramp X
48-function done(result) { return ["pure", result]; }
49-// [1,Tramp X] -> Tramp X
50-function cont(thunk) { return ["cont", thunk]; }
51-// Tramp X × [X,Tramp Y] -> Tramp Y
52-function bind(action, f) { return ["bind", action, f]; }
53-// function join(action) { return cont(() => bind(action, ma => ma)); }
54-// function fmap(f) { return action => bind(action, x => done(f(x))); }
55-// [X,Tramp Y] × [Y,Tramp Z] -> [X,Tramp Z]
56-function andThen(f, g) { return x => bind(f(x), g); }
57-// Tramp X -> X
58-function trampoline(action) {
59- // Tramp X
60- let register = action;
61- // µS. [X,Tramp Y] × S + 1
62- let stack = null;
63- while (true) {
64- switch (register[0]) {
65- case "pure":
66- if (stack === null) { return register[1]; }
67- register = stack[0](register[1]);
68- stack = stack[1];
69- break;
70- case "bind":
71- stack = [register[2], stack];
72- register = register[1];
73- break;
74- case "cont":
75- register = register[1]();
76- break;
77- default:
78- console.log("stack", stack);
79- throw new Error("unknown trampoline action " + register[0]);
80- }
81- }
82-}
40+function fixNaN(x) { return isNaN(x) ? [false, null] : [true, x]; }
41+function rescueNaN(f) { return x => fixNaN(f(x)); }
42+function rescueNaN2(f) { return ([x, y]) => fixNaN(f(x, y)); }
8343
8444 const prims = {
8545 "id": x => x,
@@ -87,6 +47,7 @@ const prims = {
8747 "fst": ([x, y]) => x,
8848 "snd": ([x, y]) => y,
8949 "dup": x => [x, x],
50+ "app": ([f, x]) => f(x),
9051 // XXX not the best encoding for sums
9152 "left": x => [true, x],
9253 "right": x => [false, x],
@@ -105,11 +66,10 @@ const prims = {
10566 "f-zero": _ => 0.0,
10667 "f-one": _ => 1.0,
10768 "f-pi": _ => Math.PI,
108- // XXX need NaN checks!
109- "f-add": ([x, y]) => x + y,
110- "f-mul": ([x, y]) => x * y,
69+ "f-add": rescueNaN2((x, y) => x + y),
70+ "f-mul": rescueNaN2((x, y) => x * y),
11171 "f-negate": x => -x,
112- "f-recip": x => 1 / x,
72+ "f-recip": rescueNaN(x => 1 / x),
11373 "f-sign": x => x <= -0.0,
11474 "f-floor": rescueNaN(Math.floor),
11575 "f-sqrt": rescueNaN(Math.sqrt),
@@ -124,11 +84,11 @@ function compile(expr) {
12484 switch (expr[0]) {
12585 case "comp": {
12686 const [f, g] = l;
127- return andThen(f, g);
87+ return x => Cammy.bind(f(x), g);
12888 }
12989 case "pair": {
13090 const [f, g] = l;
131- return x => bind(f(x), y => bind(g(x), z => done([y, z])));
91+ return x => Cammy.bind(f(x), y => Cammy.bind(g(x), z => Cammy.done([y, z])));
13292 }
13393 case "case": {
13494 const [f, g] = l;
@@ -136,20 +96,18 @@ function compile(expr) {
13696 }
13797 case "curry": {
13898 const [f] = l;
139- // XXX slightly wrong...
140- return x => done(y => f([x, y]));
99+ return x => Cammy.done(y => f([x, y]));
141100 }
142101 case "uncurry": {
143102 const [f] = l;
144- // XXX ...but also wrong here
145- return ([x, y]) => bind(f(x), g => g(y));
103+ return ([x, y]) => Cammy.join(Cammy.bind(f(x), g => g(y)));
146104 }
147105 case "pr": {
148106 const [z, s] = l;
149107 return x => {
150108 let rv = z(null);
151109 for (let i = 0; i < x; i++) {
152- rv = bind(rv, s);
110+ rv = Cammy.bind(rv, s);
153111 }
154112 return rv;
155113 };
@@ -159,9 +117,9 @@ function compile(expr) {
159117 return xs => {
160118 let rv = n(null);
161119 for (let i = 0; i < xs.length; i++) {
162- rv = bind(rv, x => c([xs[i], x]));
120+ rv = Cammy.bind(rv, x => c([xs[i], x]));
163121 }
164- return done(rv);
122+ return Cammy.done(rv);
165123 };
166124 }
167125 default:
@@ -171,7 +129,7 @@ function compile(expr) {
171129 if (prims[expr] === undefined) {
172130 throw new Error("unimplemented prim " + expr);
173131 }
174- return x => done(prims[expr](x));
132+ return x => Cammy.done(prims[expr](x));
175133 }
176134 }
177135
@@ -250,7 +208,7 @@ function tileForIndex(extractor, index, title, trail) {
250208 const canvas = document.createElement("canvas");
251209 canvas.width = 100; canvas.height = 100;
252210 div.appendChild(canvas);
253- drawFrame(canvas, ([x, y]) => trampoline(compiled([x, y])), getChannels);
211+ drawFrame(canvas, ([x, y]) => Cammy.runTrampoline(compiled([x, y])), getChannels);
254212 } else {
255213 div.innerHTML += "Not sure how to display this image yet.";
256214 }
@@ -265,7 +223,7 @@ function tileForIndex(extractor, index, title, trail) {
265223 function stepFrame(timestamp) {
266224 if (start === undefined) { start = timestamp; }
267225 const t = (timestamp - start) * 0.001;
268- drawFrame(canvas, ([x, y]) => trampoline(compiled([[x, y], t])), getChannels);
226+ drawFrame(canvas, ([x, y]) => Cammy.runTrampoline(compiled([[x, y], t])), getChannels);
269227 setTimeout(() => window.requestAnimationFrame(stepFrame), 1000);
270228 }
271229 window.requestAnimationFrame(stepFrame);
@@ -277,7 +235,7 @@ function tileForIndex(extractor, index, title, trail) {
277235 {
278236 div.setAttribute("class", "tile sequence");
279237 const result = Array.from({length: 10},
280- (_, i) => trampoline(compiled(i)));
238+ (_, i) => Cammy.runTrampoline(compiled(i)));
281239 const guts = result.map(renderer).join("</td><td>");
282240 div.innerHTML += `<table>
283241 <tr>
@@ -288,7 +246,7 @@ function tileForIndex(extractor, index, title, trail) {
288246 break;
289247 case JSON.stringify("1"):
290248 div.setAttribute("class", "tile element");
291- div.innerHTML += renderer(trampoline(compiled(null)));
249+ div.innerHTML += renderer(Cammy.runTrampoline(compiled(null)));
292250 break;
293251 default:
294252 div.innerHTML += "Not sure how to display this yet.";
--- a/shell.nix
+++ b/shell.nix
@@ -11,14 +11,14 @@ in pkgs.stdenv.mkDerivation {
1111 # debugging native code
1212 gdb checksec
1313 # profiling RPython JITs
14- python3Packages.vmprof
14+ # python3Packages.vmprof
1515 # maintaining embed/
1616 ocamlformat
1717 # maintaining movelist/
1818 egg2nix
1919 # maintaining cammy-rpy/
2020 # python2Packages.pyflakes
21- python3Packages.pyflakes
21+ python39Packages.pyflakes
2222 # using cammy-comb
2323 graphviz
2424 # using cammy-draw
@@ -30,7 +30,7 @@ in pkgs.stdenv.mkDerivation {
3030 # working with JSON
3131 jq
3232 # honey
33- python3Packages.flask
33+ python39Packages.flask
3434 # benchmarking
3535 busybox feedgnuplot linuxPackages.perf
3636 # publishing
@@ -43,9 +43,8 @@ in pkgs.stdenv.mkDerivation {
4343 sloccount
4444 # experimenting with hive management
4545 ranger
46- # ???
47- openapi-generator-cli yaml2json python3Packages.virtualenv
48- # !?!?
49- wabt binaryen
46+ # experimenting with Honey
47+ # openapi-generator-cli yaml2json python3Packages.virtualenv
48+ purescript
5049 ];
5150 }