-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathtodo.txt
182 lines (130 loc) · 13.1 KB
/
todo.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
* you could have multiple trait constraints per argument, such that you also pass in a vtable for each argument (or repack a pair struct) that is assembled by
the calling function on the stack, and which can be reduced in the number of trait vtables for that type's vtable list when recreating the vtable list to call
another function. You just can't have additional constraints if the type isn't already defined with that many. It might end up being a lot of vtable overhead
if they can't be predetermined. Can they be shrunk another way? You also wouldn't be able to store these values since the vtable would disappear after the call...
* I hate to say it, but I should maybe remove exceptions because they aren't a good way of returning *shrug* I think Haskell recommends not using them
* add functional functions like map, filter, foldl, foldr
* you currently can't call a method on a type that implements a trait. You can only call the trait methods on a universal, but that shouldn't be necessary
* you could have a scope in session which is for *all* traits, and all trait methods get defined in that table, and if method lookup fails through normal means, you can
lookup on the common traits table to find the trait_id, and then make an access to the method in the static vtable if it exists, but you'd have to maybe do more work
in transform to find these places and properly convert them
---
* it's now possible in example/binary tree to declare print_tree() as generic, but then it prints garbled data because of the issue encoding type variables embedded in another type
(eg. ref BinaryTree<'item>)
* seems to be problems implementing Display on Option<'item>, caused by check_trait_constraints() which causes a recursive loop checking Option<'item> with 'item
* the issue is with accessing the item inside the option, which causes a segfault, probably because of that issue with packing/unpacking
* fix the issue where passing a function `() -> String` into another function as `() -> 'a`, with a universal, will unpack the String value without packing it from within
the non-universal function, causing a segfault. Should we just disallow anything but a bare universal?
---
* you can't import files that contain a dash in the filename
* using a decl for a function defined in later, in the same module, causes an overload error rather than linking the definition and declaration
* the try block's pattern matching is not useful because subtyping wont let us match different parents, only inheritence chains, so we really need an exception enum to be
usable, which could mean using the `exception` keyword like in ML or make a way of extending enums
* implement trait dependence
* add some way of creating a traitobj using a def or annotation (issues with universals vs variables and mapping typevars; using check_type(..., false) works but might not be correct)
* need a way to import/compile directory of many files (libcore as many files)
* can you remove access to getindex?
* ByteString or ByteArray or ImmutableString?
* rename String type to ByteString, ByteArray, or CString, or ZString, or NullString, or NullByteArray, or ZByteArray, RawString; I'm leaning ZByteArray or ByteString
* rename SuperString type to String, will require a bunch of libcore changes
* you could also maybe add a native array type (like a slice, with a pointer and size value in a pair), to replace UniversalArray
* I'd really like to change the way method calls are done. It's annoying having all these definitions and blocks for every call
but this might not be possible because you need to bind references during type checking, and you can't do that without all the variants
DISCUSSION:
* you know... it might be possible to make all 'methods' actually be functions with a fixed type for the first argument, since functions can be overloaded, and the
type would be unique. I guess it wouldn't work for inheritence, but at least it doesn't cause this weird irregularity between classes/objects and enums (and eventually traits and trait objects)
* you need to figure out what should and shouldn't be a reference type... objects are always references that are malloced with 'new', and only objects can use 'new',
whereas enums, records and tuples are not reference types, but can be made into references with 'ref'. There will also be trait objects. Enums are already 'Type::Object'
and are distinguished by their definition, yet one a reference always and the other is not. Trait objects would implicitly always be a reference
* currently you have function overloading, and class method overloading, which are forms of ad-hoc polymorphism. Should all function overloading instead require a trait
implementation (eg. all the infix operator functions and str functions would become traits which would be implemented for different types instead of being overloaded funcs).
Class methods kind of still need to be ad-hoc.
* if you made overloaded functions implement traits instead, then you'd want to be able to call them like functions, maybe sometimes also like methods (eg. str(x) and x.str())
* would you still be able overload function names and argument numbers? You could have trait { add('a, 'a) } and also add(String, String, String), since the types of the functions
don't overlap, but you couldn't also have add(Int, Int) which isn't defined as a trait impl, since it would conflict with add('a, 'a). This might not be too different from what
you have now, as long as you make sure to check trait def types when checking for overloading
REFACTORING & IMPLEMENTATION:
* should you make a trait object's data be the first pointer (always a pointer), so that a double deref will get it, rather than an indexed deref?
* should you use a different format for trait objects vs unbound universals?
* add common constants for various context and vtable strings throughout, instead of dupilcating strings
* for Expr resolve and accessor, the oid's could maybe be integrated into the ident? same for invoke now? well now traits are using oid for a ref...
* can you make closure conversion a separate stage somehow, possibly with a MutVisitor
* can you make an exception conversion mut visitor? to add the execption argument to everything
* can you put a validation stage in that can check for overloading at the end of type checking rather than during. Trait impl checking can be moved there too
* can you remove some of the weirder ref trails (a ref to a ref to a ref), to make things simpler and more predictable? overload variants, enum variants, and traits use them
* you can maybe store more info in the defs, including the context of the function (Plain, Class Method, Trait Method), and then use that during transform
* reduce the number of clone()s
* for transformer, you could have a list of 'bodies' of code, which are appended to by the transform functions, rather than returned.
It could also store the context, but you'd have to add a body when transforming `if`, `while`, `sideeffect`, `match`, `try`, and `import`
* move the pattern transform functions into the visitor, but right now you're passing in a value_id to use for comparisons. It'd have to be stored in the transformer obj insteado
* improve storing of file info in the Pos structs (maybe rename to Span?)
* change parser to take session argument or something so it can record the file info, and maybe do other advanced things
BUGS:
* there's a possible issue with closure reference conversion, where you only check if the name exists in the global scope, but not if it's the same definition. It's
possible there could be a different variable with the same name declared in the global scope causing the local one to not be converted into a context access. But
right now, because of overloading, the def will be a specific function, and the stored scope def will be the overloaded def that contains all the possible specific impls
* there is still a bug with the Phi statement, in that it will return null if you don't cover all cases. there is no check for coverage
* unwrap error when recursively calling in `let recfoo = fn x =>` because the actual DefLocal is inserted after the initializer code is transformed, but if this was fixed,
would it be possible to access recfoo before it's been initialized, such as in `let something: Int = { something; 0 }`
Error Messages:
* make builtin types unable to inherit from, because there's no vtable
* when a trait is not implemented, it will produce an unhelpful type error that it expected random variable, but found unimplemented type
* there is no context info for the ununified variables, which makes it hard to find the issue
* you can put a restriction on type aliases such that a type can only use type vars defined in the declaration
* you might not be making sure that non-closure functions don't access variables outside their scope (methods are never closures)
* make sure that you don't overload different types of functions (closures, functions, and methods must all be the same type to allow overloading)
* should you make mutable modifiers more strict, like rust?
TESTS:
* test for static method vtable access, dynamic dispatch via that mechanism, and overloading used with resolve
* error conditions, lack of typing should error, etc
* test that non-closures accessing values outside their scope produces meaningful errors
* test module system (might need a test opt-out for submodules in a test)
Changes:
* get hashmap in libcore working
* change how you reference an operator function; like op+ or (+)
* put intermediate files somewhere else, at least as controlled by the molten script, so as not to clutter source directories
* there's no way to genericly compare or print tuples or records (maybe memcmp based comparison)
* improve library path handling
* should it be possible to not import all symbols from a module? Should there be a namespaced object thing?
* how does rust do "use" if there is a circular dependency?
* the module level scope is now a closed function, so you could return a representation of it as the "module" object
* add namespace of some kind, such that you can group functions? like 'module' from ML?? like impl from rust?? or like namespace from C
* fix lib to allow builtin classes??
* add 'import as' syntax
* add 'decl as' syntax
* add pipe operator
* make loops use phi, and add break
* add compiler directives?
* should you make it possible to call a trait method via a resolve; eg. Add::add(x, y). The issue is that vtable is in the objects
* should you make it so that only functions/classes/enumdefs/etc can create a universal, and other places like new/definition/etc will raise an error if they are undefined, is this useful?
* should you fix scope resolution to also work on objects, so that you can have static methods from the object without explicitly referencing its type
* should exports also list all the modules that the file imports, so that class definitions in other imports can be found, if needed for arguments
* should you add break/continue statements for loops
* should you require that all return types be used, unless an "ignore('a)" function is explicitly used?
* should you add some means of accessing variant values without pattern matching?
* should you change Real to Float? also should you change them to lower case names, which is in line with sml, ocmal, f#
* should you add access modifiers to classes, and maybe functions; public, private, protected
* should module init return something other than 0, like a module object? Is that even useful?
* should you add a return statement? It's not functional, but it's practial
* should you allow keyword arguments in invoke; foo(a=10, b=20)
* should you allow arguments with a default value to be omitted from a function call?, that would require a bunch of changes to typechecking
* should local variables be allowed to be mutable...
* should you require an override keyword when masking definitions in lower scopes? like class definitions
* should you add macros and expandable forms (like lisp)
* should you implement assignment of tuples and records? (in addition to the record update syntax)
* is it even possible to add a special "constrained" type, which has &AST that must type check correctly in order to match (checks done in check_type)
* what if you generalize overloaded types, such that they're checked in check_type, and options are eliminated, or resolved to one type over the course of checking
* should you implement the context method described in the proper bidirectional typechecker algorithm
* should vtables be dynamically allocated
EXAMPLE PROGRAM IDEAS:
* hash table
* regex matcher
COMPLETED TESTS (for reference):
* test for references, records, tuples, used in local vars, func args, return values, etc
* test for directly calling functions in records or tuples, which shouldn't add the object argument
* test for records that have different member orders are still equal
* test that raise inside a C function causes a syntax error
* test class init with inherited classes
* test for recursive C functions, recursive MF functions
* parse error with let bar = ref { a = 1, b = 2 } \n (!x).a
* test that you don't allow overloading of overlapping function types, and different function types (??)