Using `newtype T = MkT Int8' or equivalent only works for foreign data types whose representation is known. How do you deal with C's "time_t", for example? Or with C "struct" types? The user doesn't want to make non-portable assumptions about what padding the C compiler is going to insert.
The usual way is to use some autoconf magic or the hsc2hs preprocessor to figure it out. The key part of fptools/aclocal.m4:FPTOOLS_CHECK_HTYPE is this program: typedef $1 testing; main() { FILE *f=fopen("conftestval", "w"); if (!f) exit(1); if (((testing)((int)((testing)1.4))) == ((testing)1.4)) { fprintf(f, "%s%d\n", ((testing)(-1) < (testing)0) ? "Int" : "Word", sizeof(testing)*8); } else { fprintf(f,"%s\n", (sizeof(testing) > sizeof(double)) ? "LDouble" : (sizeof(testing) == sizeof(double)) ? "Double" : "Float"); } fclose(f); exit(0); }
But to deal with cases like the ones mentioned above, Mercury also allows the user to declare how such types are represented in foreign languages, using `pragma foreign_type'
declarations: :- pragma foreign_type("C", t, "some_c_typename").
The Mercury compiler will generate appropriate glue code to marshal and unmarshal values of that type.
How does it figure out what concrete type to use? Does it use trial compilations a la autoconf?
In fact, the "data Int" declaration need not even be physically present; the implementation could just insert it in the Prelude automatically. (That's what the Mercury implementation does for its builtin types.)
Hugs used to insert it automatically but we found that putting the declarations in the Prelude source code: 1) Made the source closer to the report and made the Prelude a little easier to understand. 2) Reduced the amount of magic required in the compiler. -- Alastair