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

Reply via email to