-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnotes.txt
364 lines (266 loc) · 9.88 KB
/
notes.txt
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
356
357
358
359
360
361
362
363
364
A type trait checks for functions that are defined for a particular type.
A Hashable trait might be implemented like this:
trait Hashable<T> {
func hash(cref T) -> uint;
};
This says that a type T is Hashable if there exists a hash function that takes
a cref to T and returns an uint.
We could allow for binary traits this
trait SwappableWith<T, U> {
func swap(ref T, ref U);
};
I'm not sure if there is any use for binary traits.
Traits can be composed. You might want your hashable type to be comparable.
trait Hashable<T> {
func hash(cref T) -> uint;
trait EqualityComparable;
};
Maybe you want a function to find the index of an element in a hash table
You might do something like this:
func hashIndex<Key: Hashable>(key: cref Key, size: uint) {
return hash(key) % size;
}
You cannot pass this function an object that doesn't have the Hashable trait.
This function only knows that the key is Hashable so it cannot assume that
the type has any other traits. For example,
func hashIndexInt<Key: Integral>(key: Key, size: uint) {
return (make uint key) % size;
}
func hashIndex<Key: Hashable>(key: cref Key, size: uint) {
// this is invalid even if hashIndex is called with an integer
return hashIndexUint(key, size);
}
The traits required by hashIndexInt would have to be a subset of Hashable for
this to work. Using traits instead of the C++ way templates work (duck typing)
means that a generic function doesn't need to be type checked every time it is
instantiated.
You could use a function to tag a type and detect the function in the trait.
type T struct {};
func myCoolTag(T) {}
trait HasCoolTag<T> {
func myCoolTag(T);
};
These tags could be used to describe a type. Since the functions are never,
actually called, they will be omitted from the executable.
Need some way of associating a type with another type.
type A struct {
first: sint;
};
type B struct {
first: real;
};
type FirstType<type A> = sint;
type FirstType<type B> = real;
Template specialization. This needs to be a part of traits somehow.
We need some way of referencing the associated type in the trait.
How do Rust and Swift do this?
Maybe...
assoc type FirstType{A} = sint;
assoc type FirstType{B} = real;
assoc let FirstTypeName{A} = "sint";
assoc let FirstTypeName{B} = "real";
trait HasFirst{T} {
type FirstType{T};
let FirstTypeName{T};
};
func getFirstName{T: HasFirst}() {
// access associated let
return FirstTypeName{T};
}
// access associated type in parametized type alias
type FT{T: HasFirst} = FirstType{T};
Maybe have a look at one of the Go generics proposals
--------------------------------------------------------------------------------
An interface defines the interface of a generic type. A generic function says
"I accept types that implement this interface" so whenever you try to call a
function with a type, the compiler will check if the type satisfies the
interface. The compiler will also check if the function uses the interface
correctly. This might be easier to implement than C++ style generics.
interface Ordered {
func lt(Self) -> bool;
};
func min<T: Ordered>(vals: [T]) -> T {
if (size(vals) == 0u) {
return make T {};
}
var elem = vals[0];
for (i := 1u; i != size(vals); i++) {
if (val[i].lt(elem)) {
elem = val[i];
}
}
return elem;
}
func swap<T>(a: ref T, b ref T) {
let temp = a;
a = b;
b = temp;
}
In provide functions for working with arrays, we're going to need some kind
of generics.
func capacity<T>(arr: [T]) -> uint;
func size<T>(arr: [T]) -> uint;
func push_back<T>(arr: ref [T], elem: T);
func append<T>(arr: ref [T], other: [T]);
func pop_back<T>(arr: ref [T]);
func resize<T>(arr: ref [T], size: uint);
func reserve<T>(arr: ref [T], size: uint);
We don't need generic types (only generic functions) to deal with arrays.
--------------------------------------------------------------------------------
A closure is a pointer to a function and a reference counted pointer to captured
variables.
struct Closure {
Function *func;
retain_ptr<Data> data;
};
The reference counted pointer makes the closure a reference type.
A closure behaves very similarly to an entt::delegate except that the a pointer
to captured variables is stored instead of an instance pointer.
entt::delegate required wrapper functions but this implementation will not
require them.
Every function is actually a member function. So called "non-member functions"
accept a "this pointer" but simply ignore it. This means that a regular function
can be stored in a closure and it will ignore the nullptr to the captured data.
A capturing lambda function will accept the pointer to the captured data and
use it.
A closure is implemented in the same way that dynamic go-style interfaces
would be implemented except that the closure holds a single function pointer
where an interface would store a pointer to a table of function pointers that
make up the implementation of the interface. If we have one, do we really need
the other?
--------------------------------------------------------------------------------
Iterators know when they have reached their end
A pseudo random number generator could have an interface like an iterator
This would allow you to do something like std::copy to fill an array with
random numbers.
An array iterator will store two pointers. This means that algorithms don't
need to take two iterators.
--------------------------------------------------------------------------------
trait RandomAccessIterator<T> {
type value_type;
func next(cref T) -> T;
func next(cref T, sint) -> T;
func prev(cref T) -> T;
func prev(cref T, sint) -> T;
func good(cref T) -> bool;
func size(cref T) -> sint;
func get(ref T) -> value_type<T>;
func set(cref T, cref value_type<T>);
}
trait RandomAccessRange<T> {
type iterator: RandomAccessIterator;
func iter(T) -> iterator<T>;
}
for (i := iter(array); good(i); i = next(i)) {
}
for i in array.iter() {
}
trait InputIterator<T> {
type value_type;
func next(cref T) -> T;
func good(cref T) -> bool;
func get(ref T) -> value_type<T>;
}
struct MTrand {
curr: uint;
};
assoc type value_type<MTrand> = uint;
func next(gen: MTrand) {
return make MTrand {gen.curr * 786784};
}
func good(gen: MTrand) {
return true;
}
func get(gen: MTrand) {
return gen.curr;
}
impl InputIterator MTrand;
var array: [uint];
resize(array, 1000);
copy(make MTrand {16}, array);
push_back iterator?
--------------------------------------------------------------------------------
A trait contains a list of:
other traits
function signatures
associated types (possibly constrained)
functions can be declared
types can be associated with other types
generic functions have template parameters that are constrained
these template parameters can be deduced from the function call
generic types have template parameters that are constained
deducing template parameters might be tricky
the user can assert that a type satisfies a trait
associating a type is different to specializing a generic type
specializing a generic type requires that the generic type is already declared
it might be possible for a trait to required a type specialzation which
requires the same trait so the two must be separate.
the compiler must determine
when a type satisfies a trait
whether the usage of a generic parameter matches the trait
whether a trait is a subset of another (when a generic function calls another
generic function)
--------------------------------------------------------------------------------
think about the rules for function overloading and shadowing. Overload across modules?
should mutability be part of the type system properly? What we have now just lets use make variables constant
deal with user types void *
functional switch
cache some stuff in the AST. Benchmark
consider using ORC
help optimizer with switch on global let
trivially relocatable optimizations
This is could be part of the standard library
func (self: sint) to_string() -> [char]
USE A STELA SCRIPT TO DEFINE A VECTOR SPRITE
deduce the type of InitList in all places that C++ can
a closure can capture the array that contains it
if closure creates wrapper functions for storing function pointers,
the wrapped function will be inlined and calls directly to the wrapped
function will be faster
can we inline a particular function call?
use memcmp when possible
optimize for trivially copyable structs that fit in registers
treat the return parameter as an lvalue (NRVO)
maybe ast::Type uniquing?
maybe we shouldn't inline so aggressively
swap operator? :=:
expose reference counted pointers
arrays should be value types
cref
maybe cref could be a little smarter than const &
for generic programming, it would be really handy if cref sint just passed
sint by value.
cref can bind to an rvalue like it can in C++
separate function signature from functions, lambdas and function types
there are a lot of functions that only need to know the function signature
but actually take sym::FuncParams or ast::Func.
This is a mess!
consistent naming
Type -> Ty
is ptr_union a good idea?
we can remove a few dynamic casts
malloc returns a pointer with alignment of std::max_align_t
range for loops might speed up iteration
IDE extensions
pretty printing
syntax highlighing
separate the two
intellisense
Have a look at Wren and Crystal
Write some programs to benchmark the compiler and language
Project Euler
Computer Language Benchmarks Game
Physics simulation
Genetic algorithm
Use a stack allocator for the AST
64 bit floats and ints
convert call to reg.view{A, B}() to runtime view invokation
a string type that is optionally owning
useful for concatenting strings in reflection
char *data, size_t size
using bit in data pointer to determine view vs own
char *data, size_t size, size_t cap
using cap == 0 to determine view vs own
error messages when we
compare a user type that doesn't have an operator==
try to copy a unique_ptr