As you know, most scripting languages are dynamically typed.
Felix is different, because it is statically typed.
Nevertheless, run time typing is not just useful,
it is essential for the proper operation of the garbage
collector, which needs at least two things:
(a) it needs to know how to destroy an object
(b) it needs to know where all the pointers are
The current system has a way to handle this: the
shape object records the offsets of pointers,
and it records the finaliser (destructor) if one
is required (pod don't require finalisation).
However the arena allocator requires more: it moves
objects using memcpy, and adjusts pointers everywhere
to account for the move. This works for many data types,
but not all. For example a C++ doubly linked list doesn't
contain Felix pointers, and memcpy of one node will
trash the system -- the pointers to that node in the
next and previous objects (if they exist) must be
adjusted.
To allow this for arbitrary C++ objects, we need to
provide a slot for a move function.
However this isn't enough in general. For example,
realloc() may move objects. In this case, we can't
call the move function, because the source is already
gone. Instead, we have to post-adjust the system in-place.
The same is true if we used mmap to remap the address at
which the object resides -- which realloc may actually
do internally.
Another demand might be to create default objects,
to assign an object, or to copy it. So my first thought
is to replace the existing finaliser function pointer
with this:
struct GC_EXTERN gc_behave_t
{
void (*init)(collector_t*, void*); ///< default constructor
void (*copy)(collector_t*, void*,void*); ///< copy constructor
void (*assign)(collector_t*, void*,void*); ///< copy assignment
void (*move_new)(collector_t*, void*,void*); ///< move operator
void (*move_assign)(collector_t*, void*,void*);///< move operator
void (*post_remap)(collector_t*, void*,void*); ///< post remap adjust
void (*pre_remap)(collector_t*, void*,void*); ///< pre remap adjust
void (*finaliser)(collector_t*, void*); ///< destructor
}
As you may know, many Functional programming languages used
a technique called 'boxing' whereby a pointer is used to
represent an object. This allows so called 'intensional
polymorphism', which manipulates aggregates of objects
independently of their types.
This is actually a hack, although FPL people are likely
to be defensive about it. It works because all the above
operations on pointers are universal. Boxing costs one
extra pointer and a deference, but it can often be
quite efficient. However C++ is more general: it supports
boxing (pointers) but provides extensional polymorphism
(expanded types) at compile time.
Using the data from a type behaviour descriptor above,
we obtain a universal set of operators WITHOUT boxing
as in C++ .. but we can do the operations at run time.
We can also augment the set of functions, to optionally
support other operations such as comparisons. This is how
scripting languages like Python actually work.
With such a setup, we effectively get 'Python' with
a builtin capability to generate 'native code extensions'
directly 'in language' using Felix. In particular, the
compiler can generate the tables above, and possibly more
such as comparisons, with hints as to which operations
are available (don't generate less<T> for a type which
doesn't have it!)
The biggest problem here is combinatorics.
For a start, the 'default' copy method is just memcpy
with a particular length. Some objects aren't copyable
at all. Some objects require more. For an aggregate,
it isn't entirely pleasant to generate a copy method
by calling the copy methods of all the components.
For a start if they're all just memcpy, a single
memcpy would do.
In some ways the 'obvious' solution is a C++ metatype
object with virtual functions.
In particular the current garbage collector uses a fixed
scheme for finding pointers -- an array of offsets.
But we could use a bitmap (most collectors do, because
the fanout is higher and it is faster .. the code is
just slightly harder to write).
And we could allow for an 'arbitrary' function to
find the pointers.
The important thing about such reflection capabilities
being polymorphic is that it allows for intensional
polymorphism .. at the possible cost of a runtime
failure. Normally, we hate that .. but it does allow
dynamic loading of compiled polymorphic routines.
The current Felix system of polymorphism, on the other
hand, is extensional and compile time only.
The *fun* part however isn't introspection .. but
run time type construction.
--
John Skaller <skaller at users dot sf dot net>
Felix, successor to C++: http://felix.sf.net
-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
Felix-language mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/felix-language