Gavin Sherry wrote:
>
>..
>
> By introducing another layer of abstraction we will most probably decrease
> the performance of all languages[1].
Some bindings require high performance and some don't. Python's binding
to Tk still goes through intepreter Tcl strings and complaints about the
performance are few and far between. Sure, your Oracle driver is going
to be language-native, but your binding to some weirdo OODBMS might be
shared and jointly developed with someone interested in another
language. Given the choice of using a loose, low-performance binding and
none at all, you might choose the loose binding.
And if the loose binding was easy enough to implement, the PHP-Sablotron
programmer might just decide to implement both the native binding and
the loose binding just to be nice.
I'll include a strawman but keep in mind that this is a very rough first
cut. I use some C++ language features to keep it syntactically simpler
than using plain structs and function pointers but that's just for
expository purposes. A cross-lang API would have to be ANSI C.
> [1] The situation where you could perhaps increase performance is the
> converse of what is being suggested. Presumably, if it is in the interest
> of language developers to have a unified extension layer, then the point
> of different which language developers want to maintain with with other
> languages is the syntax/grammar of the language.
It is not (yet?) true that the only difference between languages is
syntax/grammar. There are real semantic differences and these
differences go right into the core of the interpreters. Finalization and
thread models are examples of choices that go to the core of the
interpreter.
Here's the straw man for an integration api for dynamic languages.
typedef enum value_type{
dia_INTEGER,
dia_FLOAT,
dia_ASCII,
dia_UNISTRING,
dia_BYTESTRING,
dia_ARRAY,
dia_HASH,
dia_OBJECT
} value_type;
typedef int i4;
//struct dia_val;
typedef struct dia_val dia_val;
typedef dia_val dia_array;
typedef dia_val dia_object;
typedef dia_val dia_hash;
typedef dia_val dia_function;
typedef struct dia_array_interface dia_array_interface;
typedef struct dia_hash_interface dia_hash_interface;
typedef struct dia_object_type dia_object_type;
struct dia_type{
value_type val_type();
int as_integer(dia_val *v);
float as_float(dia_val *v);
char *as_ascii(dia_val *v);
char *as_bytestring(dia_val *v);
i4 *as_unistring(dia_val *v);
dia_array_interface *array_interface(dia_val *v);
dia_hash_interface *hash_interface(dia_val *v);
dia_object_type *object_type(dia_val *v);
long long identifier(dia_val *v);
void free(dia_val *v);
};
/*
A note on accessors that return pointers: the pointers should be good
for as long as the dia_val object is alive and the val object is alive
until it is freed.
It is illegal for the caller to attempt to fetch an integer wih the
type is other than dia_INTEGER or float when the type is other than
dia_FLOAT. Nevertheless, the dia_type implementation is required to
return MAXINT when this error occurs rather than a random value.
If an inappropriate pointer value is fetched, it should be null.
The caller should only pass a dia_val to functions on the dia_type
referenced by its type pointer. The dia_type functions should also
check this and return null if they are used inappropriately.
*/
struct dia_val{
void *_value;
dia_type *type;
char *exception;
};
/*
A value that represents an exception should have a very short string
representing the exception's type. A more descriptive string
describing the exceptional situation should be retrieved like this:
val->type->as_ascii(dia_val)
*/
struct dia_hash_interface{
dia_hash *dia_copy_hash(dia_hash *v);
dia_val *get_value(dia_hash *v, char *key);
dia_val *set_value(dia_hash *v, char *key, dia_val *val);
dia_val *del_value(dia_hash *v, char *key);
};
struct dia_array_interface{
dia_array *dia_copy_array(dia_array *v);
dia_val *get_value(dia_array *v, int position);
dia_val *set_value(dia_array *v, int position, dia_val *val);
dia_val *del_value(dia_array *v, int position);
};
struct dia_function_interface{
dia_val *call(dia_val *args); // typically args is array or hash
// these are optional...
char *name(dia_val *func);
char *file(dia_val *func); // url or absolute path
int lineno(dia_val *func);
char *description(dia_val *func); // raw text -- if any
}
struct object_type{
char *name(dia_object *o);
dia_val *get_attribute(dia_object *o, char *attrname);
dia_val *set_attribute(dia_object *o, char *attrname,
dia_val *val);
dia_array *list_attributes(dia_object *o);
// gets a *bound* method -- it already knows what object it
// goes with. Do NOT pass the instance as the first param!
dia_function *get_method(dia_object *o, char *methodname);
dia_array *list_methods(dia_object *o);
};
/*
Notes:
There should be sprintf-style helper functions to make arg array and
hash building easy.
Functions are values so an object could have attributes that are
functions. Method-centric functions are still provided because in some
languages, methods are different than function attributes.
The set of names of methods and attributes should be disjoint.
*/
--
Take a recipe. Leave a recipe.
Python Cookbook! http://www.ActiveState.com/pythoncookbook