-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexamples.stream
355 lines (264 loc) · 7.98 KB
/
examples.stream
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
// NOTICE: this file is just some random notes and snippets of stream code that I've created over a few months.
// A lot of the spec has changed since a lot of this code was written, so much of it is out of date and actually invalid syntax.
// There's some good stuff in here, but a lot of it is old.
function_call = {
= +config = Fs.read[:'config.txt'] =>{} Json.decode{.opt: 123}
<= +config <= Fs 'read' {:'config.txt'} =>{} Json 'decode' {.opt: 123}
<= +config <= (((Fs 'read') {:'config.txt'}) =>{} ((Json 'decode') {.opt: 123}))
<= +config <= (((Fs 'read') {:'config.txt'}) =>{} ((Json 'decode') {.opt: 123}))
<= Fs.write <- {:'config.txt'} <= .data: Json.encode{.opt : 123} <- config
+a = (1)
+b = (2)
a <= b
}
// How do actor input and outputs work?
// Variables this, in, and out are provided
// Piping or reading from nothing aliases in
// Piping or reading to nothing aliases out
// Value comparison
<~ !>
>~ !<
<> !~
~
// Object comparison
==
!=
A -> B
C => D
// If A updates, run all B with in=A
// If B updates, run new B foreach A with in=A
// If C updates, add new C to D
// If D updates, do nothing
.abc == .abc // False
.abc ~ .abc // True
// Throw
stream -> !is_int -> Error(Error.high, 'Argument must be an integer')
// Default arguments
+args = {=> +target
<= { => +source
source.keys -> {
source in => target in
}
}
}
+func = {-> args{.argA: +a, .argB: +b}
}
// Splitting a stream
+my_stream
+a, +b
+split = {=> +obj => (if(obj.num >= 0) {a} else {b})}
split <- my_stream
// Whenever a new value is sent into my_stream, send it to split.
// Split creates a new stream (+obj)
// Split evaluates the parenthesis
// obj is currently an empty set.
+if
+elif
+else
{
if = {=> cond
cond #
}
}
// Comparison operators return first argument if true, or empty set if false
// Cloning a stream
my_stream => +my_stream
// Joining a stream on the i key
// Sorting a stream
+my_stream = (6,3,4,2,1)
my_stream -> {
+max
+i = 0
my_stream -> {=> +cur
cur !> max[-1].el # ~ 0 -> {
max <= {.i: i, .el: cur}
}
i <= i + 1
}
} -> +sorted_stream
+range = {-> args[.start: +list, .end: +end, .step: +step]
step #=0 -> {step = 1}
<= list < end -> {in + step => list, =>}
}
+repeat = {=> +num
= {=> +func
<= range{start: 0, end: num} -> func
}
}
+while = {=> +cond
= {=> +body
+list = 0
<= cond list #>0 -> {in -> list, body in =>}
}
}
// Python list comprehension:
// S = [2*x for x in range(100) if x**2 > 3]
+S = range(0, 100) -> {in * in > 3 =>} -> 2*
// Native operators:
= <= =>
<- ->
{} [] ()
,
'' ""
+
#
// Lazy
+ints = (0, -1, 1)
ints > 0 -> {ints <= (-in - 1, in + 1)}]
+is_prime = {
<= in % range(2, sqrt(in)) ~ 0 #~0 -> [in =>]
}
+primes = ints > 2 < 1000 -> is_prime
// Classes
+Class = {
.get_a : {'a' =>}
.get_b : {'b' =>}
}
// Read from a file, parse as csv, sort by the second column, and write back to the file.
(<= FS.read[:'test.txt']).lines.split[:',']
+out_stream = out
(out_stream <= Fs.read <- ['test.txt']).lines -> {=> +line
out_stream <= Console.write <- line.upperCase()
}
/*
todo
control structures
continuous assertions
assertions / errors / exceptions
It would be really nice if streams were un-ordered. Then, programmers wouldn't be tempted to write functions to access a stream item at an index.
We don't want this because it should be easy for a programmer to change from access by i to access by name or another property.
*/
// Creating a setter
// A => B
// A -> |B|
+prop
|prop| = {-> 0.is_super =>}
|prop| = 0.is_super
2 => prop
+num <= prop
// |stream| returns a stream that contains one function, when piped to, appends to the original stream.
// ->[] pipes to the next [], and likewise with {} and (), and with =>
// A: B is a native language construct for 2 reasons:
// So it can be used in a function, and the internal "in" variable accesses the containing function's "in".
// So it's precedence can be lowered so that "A: B C" evaluates to "A: (B C)" instead of "(A: B) C"
// "A: B" converts to "-> {in ~ A} -> {B =>} =>, -> {in == keys} -> {A =>} =>"
// [...] and {...} both enclose functions, but only {...} creates a new scope. In addition, inside of [...], only the empty values, not in or out, are set.
// Standard convention should be to use [...] for function arguments
// File reading / writing
(<= FS.read[:'test.txt']).data -> ...
(<= FS.write[:'test.txt']).data <- ...
// Type extension
+make_type = {{
+extensions
.extend: {
out = make_type[]
out.extend (in, extensions)
}
.new: {
{
-> extensions =>
}
}
}}
+Type = make_type[]
+Child = Type.extend {
.get_abc: {'abc' =>}
}
+inst = Child.new
// Super-last?
+stream
stream $ -> [1 => stream]
stream $ -> [2 => stream]
// Here, both `$` operators would pass at the same time, and the result would be unordered.
// However, here the result would be ordered:
+stream
stream $ -> [1 => stream]
stream $$ -> [2 => stream]
// Streams are ordered, however the writing operations may run in any order. So "+abc = (1, 2)" is un-ordered.
// However, there could be a blocking operator that returns a reference to the input stream when the stream is complete.
// Would only return the first time the stream completes though.
// Can only write to in-scope streams.
// No, can write anywhere.
+get_c = {
<= +c
}
+a
{
+b
a <= 1 // Fine
b <= 2 // Fine
get_c[] <= 3 // Error
}
func[...] - Binds arguments and returns known values but will not cause side-effects or mutate any objects. Can call external objects but cannot cause side-effects.
func(...) - Same as above but can cause side-effects or mutate objects.
Function calls must be accomplished with a single message. Otherwise, multiple actors calling the same function will conflict.
Chaining operations?
Returns a promise that will resolve to the file contents
Pass implicit sets by reference???
Precedence:
1. Parenthesis
2. Implicit left to right
3. Explicit single left arrows right to left, and explicit single right arrows left to right
4. Explicit double left arrows right to left, and explicit double right arrows left to right
<= {
function_call <- (...) <- {. == this} <- {
}
}
A B
Foreach element a in A:
a <- B
When an actor replies with an object, it can be consumed by its caller. If it not consumed, it automatically replies and propogates up the call chain.
A list does not create its own scope. Variables declared inside of it enter the parent scope.
Once a variable is declared, it must be assigned an object immediately.
That object cannot be changed unless the variable is re-declared - Need a mutable keyword to do this
There exists a Box type that wraps another type, like a pointer
+obj = 123
obj = 456 // not an error
obj.inc()
(1, 2, a=3)
([key = '', val = 1], [key = '', val = 2], [key = 'a', val = 3])
{-> (+a, +b, a=+c)}
{-> ([key = '', val = +a], [key = '', val = +b], [key = 'a', val = +c])}
fib = {
==1 1
1 fib
}
(+var)
{==''}
set is everything
(a, :b 2)
+Object = {(+in1, :a +in2) -> obj1 -> obj2 -> (+out1, +out2)
=='=' { => (, +source); }
}
<= my_stream # -> [
true: 123
false: 456
]
External:
Processes
Get process adrguments
Kill/start processes
Console output
Timeout callback
FS
Class loader
Sockets
Protocols (HTTP, FTP, MySql)
OS interface
1. Sets are the only collection
2. Scoped properties (with pointers / references)
3. Everything is an object, no control structures
4. Code == Data
5. Strings are just numbers
6. Functions can be surrounded by brackets or started with a colon
7. Asynchronous like javascript
8. Executes code in callers scope, for exceptions and control structures
9. String quotes can have any number of characters: '''''abc'''''
10. Property keys are any objects, not just strings
11. Forget multithreading for now
12. No this, only arguments
13. Query methods that cannot cause side-effects ???
14. Single generic control structure
15. Continuous assertions on a variable
16. Only un-named arguments if they can be re-ordered without affecting function call
17. Debug log messages like "File contents looks like a filename"