184
|
1 |
--------------------------------------------------------------------------- |
|
2 |
-- Alternative 4: first-class messages, in <angle brackets>. Not quite |
|
3 |
-- objects, because sends (message object) reduce to (object message), |
|
4 |
-- and obviously (object object) reduces normally, but (message message) |
|
5 |
-- is an error." |
|
6 |
|
|
7 |
-- Here, .foo has no protocol at all, not even the implicit visitor |
|
8 |
-- protocol (.foo bar) --> (bar .foo) used earlier. Atoms are thus |
|
9 |
-- totally inert - they're just simple tags with no behaviour. Messages, |
|
10 |
-- on the other hand, have the meaning <x y z> --> {r -> r x y z} except |
|
11 |
-- that the x,y,z is evaluated before being closed over - so more like |
|
12 |
-- <x y z> --> let (x1,y1,z1) = (x,y,z); {r -> r x1 y1 z1}. |
|
13 |
|
|
14 |
-- Hey, that's neat - <> is the identity object! |
|
15 |
-- and <+ 2> is different from (2 +)... |
|
16 |
|
|
17 |
-- Using <> in a pattern causes a visitor-probe to be sent to the object |
|
18 |
-- being matched. Obviously, it has to be expecting such a probe, so |
|
19 |
-- data objects (ie algebraic data type instances, as opposed to |
|
20 |
-- functions or other kinds of receiver) should be coded with |
|
21 |
-- visitor-DNU-behaviour, |
|
22 |
|
|
23 |
-- {...; visitor -> visitor .thisIsWhatIam (someArg, otherArg)} |
|
24 |
|
|
25 |
-- Note that the sends to the visitor must occur in tail position. (What |
|
26 |
-- happens if they're not?) |
|
27 |
|
|
28 |
-- A visitor-probe for the pattern <.foo bar> looks like |
|
29 |
|
|
30 |
-- { .foo bar -> ...continuation of matching...; |
|
31 |
-- _ -> ...backtrack to next clause... } |
|
32 |
|
|
33 |
-- So the case below, {acc, <.s:empty> -> acc; |
|
34 |
-- acc, <.s:next(f, r)> -> loop(fn(f, acc), r)}, |
|
35 |
-- 'expands' to a match tree like |
|
36 |
-- - match tuple |
|
37 |
-- - length 2 |
|
38 |
-- - element 0: accept any, bind to 'acc' |
|
39 |
-- - element 1: visitor-probe { |
|
40 |
-- .s:empty -> run continuation 'acc' |
|
41 |
-- .s:next -> - match tuple |
|
42 |
-- - length 2 |
|
43 |
-- - element 0: ... |
|
44 |
|
|
45 |
-- What happens when there are two visitors in a tuple? |
|
46 |
|
|
47 |
-- {<.s:empty>, <.s:empty> -> .bothEmpty; |
|
48 |
-- _, <.s:empty> -> .secondEmpty; |
|
49 |
-- <.s:empty>, _ -> .firstEmpty; |
|
50 |
-- _, _ -> .neitherEmpty} |
|
51 |
|
|
52 |
-- It basically needs to backtrack. It should sort the branches |
|
53 |
-- appropriately before building the match tree: |
|
54 |
|
|
55 |
-- {<.s:empty>, <.s:empty> -> .bothEmpty; |
|
56 |
-- <.s:empty>, _ -> .firstEmpty; |
|
57 |
-- _, <.s:empty> -> .secondEmpty; |
|
58 |
-- _, _ -> .neitherEmpty} |
|
59 |
|
|
60 |
-- (and I'm thinking of the more general case here, where you might fail |
|
61 |
-- to match in the second element of the tuple, necessitating a |
|
62 |
-- backtrack out of the match in the first element. Something like {(1, |
|
63 |
-- 2) -> .a; (_, _) -> .b} when presented with (1, 3).) |
|
64 |
|
|
65 |
namespace s = "http://eighty-twenty.org/etng/r1/ns/stream#"; |
|
66 |
define s:cons(f,r) = s:StreamOperations / <.s:next(f, r)>; |
|
67 |
define s:nil = s:StreamOperations / <.s:empty>; |
|
68 |
|
|
69 |
define s:StreamOperations = [ |
|
70 |
.s:foldl acc fn -> <(acc, self)> [acc, <.s:empty> -> acc; |
|
71 |
acc, <.s:next(f, r)> -> self(fn(f, acc), r)]; |
|
72 |
.s:foldr acc fn -> <self> [<.s:empty> = acc; |
|
73 |
<.s:next(f, r)> -> fn(f, self(r))]; |
|
74 |
.s:do fn -> <self> [<.s:empty> = .:ok; |
|
75 |
<.s:next(f, r)> -> do fn(f); self(r)]; |
|
76 |
|
|
77 |
.s:inject -> self.s:foldl; |
|
78 |
]; |
|
79 |
|
|
80 |
[1, 2, 3].s:do println; |
|
81 |
[1, 2, 3].s:inject 0 {acc, each -> acc + each}; -- 6 |
|
82 |
[1, 2, 3].s:inject 0 (binop +) -- 6 |
|
83 |
[1, 2, 3].s:map (1 +) -- [2, 3, 4] |
|
84 |
[1, 2, 3].s:map <+ 2> -- [3, 4, 5] |
|
85 |
[1, 2, 3].s:map .:negated -- an error |
|
86 |
[1, 2, 3].s:map <.:negated> -- [-1, -2, -3] |
|
87 |
|
|
88 |
-- [a, b, c] is shorthand for stream:cons(a, stream:cons(b, stream:cons(c, stream:nil))) |
|
89 |
-- [a, b | c] is shorthand for stream:cons(a, stream:cons(b, c)) |
|
90 |
-- [| c] is thus 'shorthand' (longhand??) for c |
|
91 |
-- [a, b, c | ] is equivalent to [a, b, c] |
|
92 |
-- thus [|] is the empty stream, stream:nil |
|
93 |
-- When used in patterns, these shorthands are expanded into appropriate visitors. |
|
94 |
-- Reserving '|' in this way gives us a nice hook for stream comprehensions later on. |
|
95 |
[ x || x <- [1, 2, 3, 4], x.number:isEven ] |
|
96 |
|
|
97 |
java:set myObj.field (newValue); |
|
98 |
myObj.field; |
|
99 |
myObj.method(argument); |
|
100 |
|
|
101 |
-- Is it a good idea to treat everything as a tuple, with most values |
|
102 |
-- being tuples of length 1? |
|
103 |
|
|
104 |
-- Simultaneous binding: pat#pat |