|
NAME="GENERATOR"
CONTENT="Modular DocBook HTML Stylesheet Version 1.64
">7. Type signatureThe %fun statement starts a new procedure specification. Green Card supports two sorts of C procedures: ones that may cause side effects (including I/O), and ones that are guaranteed to be pure functions. The two are distinguished by their type signatures. Side-effecting functions have the result type IO t for some type t. If the programmer specifies any result type other than IO t, Green Card takes this as a promise that the C function is indeed pure, and will generate code that calls unsafePerformIO. The procedure specification will expand to the definition of a Haskell function, whose name is that given in the %fun statement, with two changes: the longest matching prefix specified with a %prefix statement is removed from the name and the first letter of the remaining function name is changed to lower case (Section 8.3 elaborates). Haskell requires all function names to start with a lower-case letter (upper case would indicate a data constructor), but when the C procedure name begins with an upper case letter it is convenient to still be able to make use of Green Card's automatic fill-in facilities. For example:
would expand to a Haskell function openWindow that is implemented by calling the C procedure OpenWindow.
would also expand to a Haskell function openWindow, but is implemented by calling the C procedure Win32OpenWindow. 7.1. Parameter marshallingThe %call statement tells Green Card how to translate the Haskell parameters into C values. Its syntax is designed to look rather like Haskell pattern matching, and consists of a sequence of zero or more Data Interface Schemes (DISs), one for each (curried) argument in the type signature. For example:
This %call statement binds the C variables x, y, z, and s, in a similar way that Haskell's pattern-matching binds variables to (parts of) a function's arguments. These bindings are in scope throughout the body and result-marshalling statements. In the %call statement, ``float'', ``int'', and ``string'' are the names of the DISs that are used to translate between Haskell and C. The names of these DISs are deliberately chosen to be the same as the corresponding Haskell types (apart from changing the initial letter to lower case) so that in many cases, including foo above, Green Card can generate the %call line by itself (Section 8). In fact there is a fourth DIS hiding in this example, the (_,_) pairing DIS. DISs are discussed in detail in Section 9. 7.2. The bodyThe body consists of arbitrary C code, beginning with %code. The reason for allowing arbitrary C is that C procedures sometimes have complicated interfaces. They may return results through parameters passed by address, deposit error codes in global variables, require #include'd constants to be passed as parameters, and so on. The body of a Green Card procedure specification allows the programmer to say exactly how to call the procedure, in its native language. The C code starts a block, and may thus start with declarations that create local variables. For example:
Here, x and y are declared as local variables. The local C variables declared at the start of the block scope over the rest of the body and the result-marshalling statements. The C code may also mention constants from C header files, such as GREY above. Green Card's %#include directive tells it which header files to include (Section 11).
7.3. Result marshallingFunctions return their results using a %result statement. Side-effecting functions, ones whose result type is IO t, can also use %fail to specify the failure value. 7.4. Pure functionsThe %result statement takes a single DIS that describes how to translate one or more C values back into a single Haskell value. For example:
As in the case of the %call statement, the ``float'' in the %result statement is the name of a DIS, chosen as before to coincide with the name of the type. A single DIS, ``float'', is used to denote both the translation from Haskell to C and that from C to Haskell, just as a data constructor can be used both to construct a value and to take one apart (in pattern matching). All the C variables bound in the %call statement, and all those bound in declarations at the start of the body, scope over all the result-marshalling statements (i.e. %result and %fail). 7.5. Arbitrary C resultsIn a result-marshalling statement an almost arbitrary C expression, enclosed in braces, can be used in place of a C variable name. The above example could be written more briefly like this: [1]
The C expression can neither have assignments nor nested braces as that could give rise to syntactic ambiguity. 7.6. Side effecting functionsA side effecting function returns a result of type IO t for some type t. The IO monad supports exceptions, so Green Card allows them to be raised. The result-marshalling statements for a side-effecting call consists of zero or more %fail statements, each of which conditionally raise an exception in the IO monad, followed by a single %result statement that returns successfully in the IO monad. Just as in Section 7.3, the %result statement gives a single DIS that describes how to construct the result Haskell value, following successful completion of a side-effecting operation. For example:
Here, a pairing DIS is used, with two int DISs inside it. The arguments to the int DISs are C record selections, enclosed in braces; they extract the relevant information from the WindowInfo structure that was filled in by the GetWindowInfo call. [2] The %fail statement has two fields, each of which is either a C variable, or a C expression enclosed in braces. The first field is a boolean-valued expression that indicates when the call should fail; the second is a (char *) value that indicates what sort of failure occurred. If the boolean is true (i.e. non zero) then the call fails with a userError in the IO monad containing the specified string. For example:
The assumption here is that fopen puts its error code in the global variable errno, and errstring converts that error number to a string. UserErrors can be caught with catch, but exactly which error occurred must be encoded in the string, and parsed by the error-handling code. This is rather slow, but errors are meant to be exceptional. Notes
|