Erick raise this issue again on felix-impl, and it needs to be addressed. 1. Unique exhibit of modular code -----------------------------------
This is a law which states that a module can only be put into a program once. You can't write: module A { .. } module A { .. } When the text is explicitly wrapped in a module statement, this law is enforced. However the text can be included twice under different module names. Because different parts of a program are written independently, access to the same module may be required. To work around the fact duplicates aren't allowed Felix has the following mess: (a) you put the module A in a file fA.flx (b) you then use the statement include "fA"; to uniquely include the file. The file is recursively and uniquely pre-processed to a post-macro processing AST input to the desugaring stage which linearises the program (removes nested functions). In fact fA is a package, and may contain more than one module, and, it may contain 'global' code too. Felix conveniently caches the AST in the file fA.par next to the original, if it has write access, and loads that file if it is up to date. To ensure uniqueness, Felix keeps a list of included package filenames. Note that macros of all kinds: #macros, lexer macros, and syntax macros are not imported in this kind package: they're localised to the package at least. 2. Localisation of syntax macros --------------------------------- Syntax macros (macro fun etc) obey scopes. For example: { macro val x = 1; .. }; { macro val x = 2; .. }; This is because syntax macros are processed after parsing, and parsing creates tree structures. This also includes 'include "fA";' kinds of packages, even though the effect of this is to *remove* tree structure in desugaring. In other words juxtaposition of two includes: include "fA"; include "fB"; where // file fA.flx module A { .. } macro val a = .. // file fB.flx module B { .. } macro val b = .. results in the concatenation (flattening) of the contained non-macro statements: module A { .. } module B { .. } but the *elimination* of the macros a and b. In particular the two parse trees are merged by chopping off their separate root nodes (so the containers root node owns the contents of both, as well as any other statements in the file other than the includes). So, ordinary statements "do not obey include package scopes" but macros "obey package scopes". In particular, code in a file X including a package Y can not only refer to the code in Y -- that after all is the point of including it -- but code in Y can also refer to code in X: the 'inclusion' is seamless, except that it must be balanced. This leads to an internal inconsistency: you can do module A { include "X"; } module B { include "X"; } but it doesn't actually work: one of the modules, probably B, will be empty. On the other hand any macros defined in an include package are lost and have to be repeated again, because for macros, the file boundaries both for inclusion and being included are barriers. 3. Hacking a solution ---------------------- A hacked up solution bans 'include'. Instead you use #import, and put the macros wanted plus an include "fA"; statement into that file. The macros are then duplicated, but the non-macro symbols are uniquely imported because a duplicated include directive has no effect. As an example, you #import <flx.flxh> wherever you need it. It is needed in each file, but only once. #include does not grab #macros, whereas #import does, both of them grab syntax macros. Consequently syntax macros propagate, whereas #macros only propagate across file boundaries. Lexical macros propagate across both. Roughly the intent was that unlike C, by using #include you can include non #macro code, but #conditional compilation and defined symbols are lexically localised to a single file. If you can't see a #define in your texted editor, a #ifdef branch won't be taken. #import was done to work around the fact this means you have to repeat common definitions, so #import allows #macros to be written once and included in each file you need it. #import also exports macros to another #importer, so you can use #import to merge #definition sets. There is a similar mess in the syntax macro processor. A macro fun may contain macros, but they're restricted to the body of that function -- they respect scope. But that prevents you packaging them up into groups, so we also have a macro proc: that's the same as a function but it ALSO exports contained macro definitions .. as well as non-macro code. 4. Binding vs Instantiation --------------------------- To make matters worse, Felix binds polymorphic functions once, but instantiates them for each call, another case of conditional uniquification. Only called functions are instantiated, and only once. Unfortunately generics require earlier type instantiation: you can't put a _tuple_fold into a polymorphic function and apply it to a type variable, because they're abstract at binding time: at instantiation time they're concretised (monorphised) but by then it is too late to bind the generic. In fact this is POSSIBLE if the generic is C++ code, because C++ binding is done even after code generation by the C++ compiler. In fact with templates this is recursive, and leads to C++ generics being unsound (although Koenig lookup aka Dependent Name Lookup tries to reduce this problem, it doesn't eliminate it). 6. Code generation ------------------ The code generation phase also does conditional uniquification. 'required' headers are emitted exactly ones, whereas ones that aren't required aren't emitted at all. 5. What we really need ----------------------- The problem is above is we have a mess of phased symbols and various groupings with inconsistent scope control. Although the idea was to get around the horrid C macro system with better structure, it is all too complicated to figure out what is defined where and how to get access to it without making a stupid mistake. C just streams #include files together and handles uniqueness with guards: this is simple and easy to use when it is enough, although the global namespace pollution is a serious problem: you get all your client header files macros whether you like it or not. A good header can #undef macros not intended for export. What we REALLY need is a SINGLE concept of Felix language with 'separate compilation' of the codes, and the ability to 'use' the code via its interface. This applies to all resources, including syntax extensions, platform flags for conditional compilation, etc. One of the problems for Felix is that unlike say Ocaml, there are no separate interfaces. Even C has separate interfaces, although the distinction isn't enforced in the language and interfaces are reprocessed for every translation unit. For this reason we have #include/#import distinction. However in modular code this is handled by a public private distinction: fun f() .. // public private fun f() .. // private and fun f() .. // not exported export f of (unit) as "f" // woops, yes it is the latter being rather inconsistent when it comes to the idea of a 'loadable library' as opposed to a mere text code library. For syntax macros we can use { } groupings for privacy, as well as include files but not #include ones, but we can use proc f() to export stuff. Here the entity defines the scope of its contents instead of the kind of call, as with private and export, and unlike #include/import. YUK. Well this is a statement of the problem spaghetti .. now we need to un-ravioli it and gnocci it into a nicer shape... which all means I'm badly in need of a coffee so that's all for now .. -- John Skaller <skaller at users dot sf dot net> Felix, successor to C++: http://felix.sf.net ------------------------------------------------------------------------- Take Surveys. Earn Cash. Influence the Future of IT Join SourceForge.net's Techsay panel and you'll get the chance to share your opinions on IT & business topics through brief surveys - and earn cash http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV _______________________________________________ Felix-language mailing list Felix-language@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/felix-language