== Refactor the Thunk and Param nodes Make Thunk a List containing (body, params) or even a map (body => node, params => [...]). Override Thunk's fmap to not fmap over the params. They can not and should not ever be reduced - it is the thunk reducer's responsibility to. * Provide type analisys * Remove and bind items from the parameter stack an App provides (perl backend) In fact, Param should not inherit Blondie::Node at all (but a constructor should still be exported from Blondie::Nodes, or incorperated into the thunk constructor). Affected code: * placeholder type inferrencing * thunk type constructor * reduce_thunk, reduce_param and possibly reduce_app everywhere This frees the Seq node to mean what it really means, instead of being the hack it is. The perl interpreter can have its own type of prim, and can compile thunks into a seq that pops params of the stack, but that sounds more work than the efficient version ;-) = Add support for list types Initially implement with perl arrays in Vals, e.g. Val([1, 2, 3]) This requires some modification of the type constructors = Make the example interactive Provide a prompt to choose a runtime All runtimes must provide ->run, so just use that Generalize the Perl::Silly backend to be Blondie::Backend::OpMasking, that can wrap any backend and filter the ->provides method call to illustrate hash replacing. = Medium == AST level branching Right now if prelude builtin provides thunks to the operator. The ternary operator is actually pretty broken - it evalutaes both its params, and then returns one, so if takes thunks, evaluates the condition, asks ?? !! to return one of the thunks, and then applies that thunk. We need support for branching at the top level of the AST for various analyses, and it also makes much more sense. However, if, &&, ||, c-style-for-loop, while etc are all *functions* that are implemented in terms of the branch node. This way they can be implemented circularly, and the emitter may do whatever it pleases at the emission level, but only really needs to implement branch. Affected code: * most reducers will need a new reducer * runtimes will need to be updated == Type infer higher order functions Allow the reduction helpers for * and ** in the prelude to be statically typed A generics approach could be used for unboxing. We have to decide who is responsible (the type annotator, or the emitter). After this is done, make the annotator stop creating separate annotations for duplicate nodes and simplify the C emitter's duplicate node finding code. == Refactor type inferring code to use better constructions of the type object The type object is currently overly nested sometimes, this could be cleaned up a bit, and the functions to "resolve" types (type annotator, c emitter) could be thrown away. The needed type values: * Runtime types - these are just values circulated for the runtime, the values that come out of its Prims, and go back to its casts. * Thunk values - this is a type that has other types, including params and return value. Reduciton of apps must ensure that the node they are trying to apply is of this type. This is a higher order value and symbols as well as params can be of this type. It's essentially a tree. * Apps with no parameters can flatten the thunk type into the return value and eliminate the app all together... See the peephole optimizer. == Optimizer This is actually a simple step, but probably a lot of work. Introduce a new family of reducers that is applied between every conversion (between initial and compilation, between compilation and SSA, between SSA and type annotation, between type annotation and emission). This step will apply pluggable reducers as it sees fit by being told at what step it is. The only pluggable reducer that I think is useful right now is a peephole optimizer (which is pluggable in itself). The peephole optimizer has plugins which say to what step they apply, and what root node they need. The root node is passed to the highest priority peephole pattern, and the result is then reapplied. If the peephole pattern made no change, the next one is tried. An example pattern: peephole App(Thunk(x)) = x If $x happens to be an App(Thunk($y)) the pattern will be reapplied on its result anyway. == Variable Assignment & binding Container creation and binding of values is good for defining the prelude as a program, and (Blondie could be SSA, or could be converted by an SSA converter - either example works well). Look at the Administrative Normal Form autrijus was talking about at e.g. http://citeseer.ist.psu.edu/flanagan93essence.html == Lexical scoping I don't like dynamic scoping, but lexical scoping makes little sense in an AST with no definitions. Once we have assignment forms, we can see how lexical scoping enters the picture. == PIR emitter Coke volunteered to help... shouldn't be too hard. Share code with the C emitter - they're essentially the same. = Hard == Dynamic type inferrence my $i = int(rand(2)); # rand :: int -> float, int :: num -> int, $i :: int my @array = (1, "two"); # @array :: [ int|str ], @arrray[$i]; # int|str The type returned from the last expression must be boxed, containing the value and its type. Perhaps lessons should be learned from Dynaml. The SSA phi operation can help us generate the type UNIONS and upgrade a value into a dynamic type as late as possible to reduce the overhead.