mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-06-10 14:00:21 +02:00
Renamed to libguile-program.texi.
This commit is contained in:
parent
5ec82e960f
commit
c393de666c
2 changed files with 782 additions and 0 deletions
782
doc/ref/libguile-program.texi
Normal file
782
doc/ref/libguile-program.texi
Normal file
|
@ -0,0 +1,782 @@
|
|||
@c -*-texinfo-*-
|
||||
@c This is part of the GNU Guile Reference Manual.
|
||||
@c Copyright (C) 1996, 1997, 2000, 2001, 2002, 2003, 2004
|
||||
@c Free Software Foundation, Inc.
|
||||
@c See the file guile.texi for copying conditions.
|
||||
|
||||
@page
|
||||
@node Programming Overview
|
||||
@section An Overview of Guile Programming
|
||||
|
||||
Guile is designed as an extension language interpreter that is
|
||||
straightforward to integrate with applications written in C (and C++).
|
||||
The big win here for the application developer is that Guile
|
||||
integration, as the Guile web page says, ``lowers your project's
|
||||
hacktivation energy.'' Lowering the hacktivation energy means that you,
|
||||
as the application developer, @emph{and your users}, reap the benefits
|
||||
that flow from being able to extend the application in a high level
|
||||
extension language rather than in plain old C.
|
||||
|
||||
In abstract terms, it's difficult to explain what this really means and
|
||||
what the integration process involves, so instead let's begin by jumping
|
||||
straight into an example of how you might integrate Guile into an
|
||||
existing program, and what you could expect to gain by so doing. With
|
||||
that example under our belts, we'll then return to a more general
|
||||
analysis of the arguments involved and the range of programming options
|
||||
available.
|
||||
|
||||
@menu
|
||||
* Extending Dia:: How one might extend Dia using Guile.
|
||||
* Scheme vs C:: Why Scheme is more hackable than C.
|
||||
* Testbed Example:: Example: using Guile in a testbed.
|
||||
* Programming Options:: Options for Guile programming.
|
||||
* User Programming:: How about application users?
|
||||
@end menu
|
||||
|
||||
|
||||
@node Extending Dia
|
||||
@subsection How One Might Extend Dia Using Guile
|
||||
|
||||
Dia is a free software program for drawing schematic diagrams like flow
|
||||
charts and floor plans (REFFIXME). This section conducts the thought
|
||||
experiment of adding Guile to Dia. In so doing, it aims to illustrate
|
||||
several of the steps and considerations involved in adding Guile to
|
||||
applications in general.
|
||||
|
||||
@menu
|
||||
* Dia Objective:: Deciding why you want to add Guile.
|
||||
* Dia Steps:: Four steps required to add Guile.
|
||||
* Dia Smobs:: How to represent Dia data in Scheme.
|
||||
* Dia Primitives:: Writing Guile primitives for Dia.
|
||||
* Dia Hook:: Providing a hook for Scheme evaluation.
|
||||
* Dia Structure:: Overall structure for adding Guile.
|
||||
* Dia Advanced:: Going further with Dia and Guile.
|
||||
@end menu
|
||||
|
||||
|
||||
@node Dia Objective
|
||||
@subsubsection Deciding Why You Want to Add Guile
|
||||
|
||||
First off, you should understand why you want to add Guile to Dia at
|
||||
all, and that means forming a picture of what Dia does and how it does
|
||||
it. So, what are the constituents of the Dia application?
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
Most importantly, the @dfn{application domain objects} --- in other
|
||||
words, the concepts that differentiate Dia from another application such
|
||||
as a word processor or spreadsheet: shapes, templates, connectors,
|
||||
pages, plus the properties of all these things.
|
||||
|
||||
@item
|
||||
The code that manages the graphical face of the application, including
|
||||
the layout and display of the objects above.
|
||||
|
||||
@item
|
||||
The code that handles input events, which indicate that the application
|
||||
user is wanting to do something.
|
||||
@end itemize
|
||||
|
||||
@noindent
|
||||
(In other words, a textbook example of the @dfn{model - view -
|
||||
controller} paradigm.)
|
||||
|
||||
Next question: how will Dia benefit once the Guile integration is
|
||||
complete? Several (positive!) answers are possible here, and the choice
|
||||
is obviously up to the application developers. Still, one answer is
|
||||
that the main benefit will be the ability to manipulate Dia's
|
||||
application domain objects from Scheme.
|
||||
|
||||
Suppose that Dia made a set of procedures available in Scheme,
|
||||
representing the most basic operations on objects such as shapes,
|
||||
connectors, and so on. Using Scheme, the application user could then
|
||||
write code that builds upon these basic operations to create more
|
||||
complex procedures. For example, given basic procedures to enumerate
|
||||
the objects on a page, to determine whether an object is a square, and
|
||||
to change the fill pattern of a single shape, the user can write a
|
||||
Scheme procedure to change the fill pattern of all squares on the
|
||||
current page:
|
||||
|
||||
@lisp
|
||||
(define (change-squares'-fill-pattern new-pattern)
|
||||
(for-each-shape current-page
|
||||
(lambda (shape)
|
||||
(if (square? shape)
|
||||
(change-fill-pattern shape new-pattern)))))
|
||||
@end lisp
|
||||
|
||||
|
||||
@node Dia Steps
|
||||
@subsubsection Four Steps Required to Add Guile
|
||||
|
||||
Assuming this objective, four steps are needed to achieve it.
|
||||
|
||||
First, you need a way of representing your application-specific objects
|
||||
--- such as @code{shape} in the previous example --- when they are
|
||||
passed into the Scheme world. Unless your objects are so simple that
|
||||
they map naturally into builtin Scheme data types like numbers and
|
||||
strings, you will probably want to use Guile's @dfn{SMOB} interface to
|
||||
create a new Scheme data type for your objects.
|
||||
|
||||
Second, you need to write code for the basic operations like
|
||||
@code{for-each-shape} and @code{square?} such that they access and
|
||||
manipulate your existing data structures correctly, and then make these
|
||||
operations available as @dfn{primitives} on the Scheme level.
|
||||
|
||||
Third, you need to provide some mechanism within the Dia application
|
||||
that a user can hook into to cause arbitrary Scheme code to be
|
||||
evaluated.
|
||||
|
||||
Finally, you need to restructure your top-level application C code a
|
||||
little so that it initializes the Guile interpreter correctly and
|
||||
declares your @dfn{SMOBs} and @dfn{primitives} to the Scheme world.
|
||||
|
||||
The following subsections expand on these four points in turn.
|
||||
|
||||
|
||||
@node Dia Smobs
|
||||
@subsubsection How to Represent Dia Data in Scheme
|
||||
|
||||
For all but the most trivial applications, you will probably want to
|
||||
allow some representation of your domain objects to exist on the Scheme
|
||||
level. This is where the idea of SMOBs comes in, and with it issues of
|
||||
lifetime management and garbage collection.
|
||||
|
||||
To get more concrete about this, let's look again at the example we gave
|
||||
earlier of how application users can use Guile to build higher-level
|
||||
functions from the primitives that Dia itself provides.
|
||||
|
||||
@lisp
|
||||
(define (change-squares'-fill-pattern new-pattern)
|
||||
(for-each-shape current-page
|
||||
(lambda (shape)
|
||||
(if (square? shape)
|
||||
(change-fill-pattern shape new-pattern)))))
|
||||
@end lisp
|
||||
|
||||
Consider what is stored here in the variable @code{shape}. For each
|
||||
shape on the current page, the @code{for-each-shape} primitive calls
|
||||
@code{(lambda (shape) @dots{})} with an argument representing that
|
||||
shape. Question is: how is that argument represented on the Scheme
|
||||
level? The issues are as follows.
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
Whatever the representation, it has to be decodable again by the C code
|
||||
for the @code{square?} and @code{change-fill-pattern} primitives. In
|
||||
other words, a primitive like @code{square?} has somehow to be able to
|
||||
turn the value that it receives back into something that points to the
|
||||
underlying C structure describing a shape.
|
||||
|
||||
@item
|
||||
The representation must also cope with Scheme code holding on to the
|
||||
value for later use. What happens if the Scheme code stores
|
||||
@code{shape} in a global variable, but then that shape is deleted (in a
|
||||
way that the Scheme code is not aware of), and later on some other
|
||||
Scheme code uses that global variable again in a call to, say,
|
||||
@code{square?}?
|
||||
|
||||
@item
|
||||
The lifetime and memory allocation of objects that exist @emph{only} in
|
||||
the Scheme world is managed automatically by Guile's garbage collector
|
||||
using one simple rule: when there are no remaining references to an
|
||||
object, the object is considered dead and so its memory is freed. But
|
||||
for objects that exist in both C and Scheme, the picture is more
|
||||
complicated; in the case of Dia, where the @code{shape} argument passes
|
||||
transiently in and out of the Scheme world, it would be quite wrong the
|
||||
@strong{delete} the underlying C shape just because the Scheme code has
|
||||
finished evaluation. How do we avoid this happening?
|
||||
@end itemize
|
||||
|
||||
One resolution of these issues is for the Scheme-level representation of
|
||||
a shape to be a new, Scheme-specific C structure wrapped up as a SMOB.
|
||||
The SMOB is what is passed into and out of Scheme code, and the
|
||||
Scheme-specific C structure inside the SMOB points to Dia's underlying C
|
||||
structure so that the code for primitives like @code{square?} can get at
|
||||
it.
|
||||
|
||||
To cope with an underlying shape being deleted while Scheme code is
|
||||
still holding onto a Scheme shape value, the underlying C structure
|
||||
should have a new field that points to the Scheme-specific SMOB. When a
|
||||
shape is deleted, the relevant code chains through to the
|
||||
Scheme-specific structure and sets its pointer back to the underlying
|
||||
structure to NULL. Thus the SMOB value for the shape continues to
|
||||
exist, but any primitive code that tries to use it will detect that the
|
||||
underlying shape has been deleted because the underlying structure
|
||||
pointer is NULL.
|
||||
|
||||
So, to summarize the steps involved in this resolution of the problem
|
||||
(and assuming that the underlying C structure for a shape is
|
||||
@code{struct dia_shape}):
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
Define a new Scheme-specific structure that @emph{points} to the
|
||||
underlying C structure:
|
||||
|
||||
@lisp
|
||||
struct dia_guile_shape
|
||||
@{
|
||||
struct dia_shape * c_shape; /* NULL => deleted */
|
||||
@}
|
||||
@end lisp
|
||||
|
||||
@item
|
||||
Add a field to @code{struct dia_shape} that points to its @code{struct
|
||||
dia_guile_shape} if it has one ---
|
||||
|
||||
@lisp
|
||||
struct dia_shape
|
||||
@{
|
||||
@dots{}
|
||||
struct dia_guile_shape * guile_shape;
|
||||
@}
|
||||
@end lisp
|
||||
|
||||
@noindent
|
||||
--- so that C code can set @code{guile_shape->c_shape} to NULL when the
|
||||
underlying shape is deleted.
|
||||
|
||||
@item
|
||||
Wrap @code{struct dia_guile_shape} as a SMOB type.
|
||||
|
||||
@item
|
||||
Whenever you need to represent a C shape onto the Scheme level, create a
|
||||
SMOB instance for it, and pass that.
|
||||
|
||||
@item
|
||||
In primitive code that receives a shape SMOB instance, check the
|
||||
@code{c_shape} field when decoding it, to find out whether the
|
||||
underlying C shape is still there.
|
||||
@end itemize
|
||||
|
||||
As far as memory management is concerned, the SMOB values and their
|
||||
Scheme-specific structures are under the control of the garbage
|
||||
collector, whereas the underlying C structures are explicitly managed in
|
||||
exactly the same way that Dia managed them before we thought of adding
|
||||
Guile.
|
||||
|
||||
When the garbage collector decides to free a shape SMOB value, it calls
|
||||
the @dfn{SMOB free} function that was specified when defining the shape
|
||||
SMOB type. To maintain the correctness of the @code{guile_shape} field
|
||||
in the underlying C structure, this function should chain through to the
|
||||
underlying C structure (if it still exists) and set its
|
||||
@code{guile_shape} field to NULL.
|
||||
|
||||
For full documentation on defining and using SMOB types, see
|
||||
@ref{Defining New Types (Smobs)}.
|
||||
|
||||
|
||||
@node Dia Primitives
|
||||
@subsubsection Writing Guile Primitives for Dia
|
||||
|
||||
Once the details of object representation are decided, writing the
|
||||
primitive function code that you need is usually straightforward.
|
||||
|
||||
A primitive is simply a C function whose arguments and return value are
|
||||
all of type @code{SCM}, and whose body does whatever you want it to do.
|
||||
As an example, here is a possible implementation of the @code{square?}
|
||||
primitive:
|
||||
|
||||
@lisp
|
||||
#define FUNC_NAME "square?"
|
||||
static SCM square_p (SCM shape)
|
||||
@{
|
||||
struct dia_guile_shape * guile_shape;
|
||||
|
||||
/* Check that arg is really a shape SMOB. */
|
||||
SCM_VALIDATE_SHAPE (SCM_ARG1, shape);
|
||||
|
||||
/* Access Scheme-specific shape structure. */
|
||||
guile_shape = SCM_SMOB_DATA (shape);
|
||||
|
||||
/* Find out if underlying shape exists and is a
|
||||
square; return answer as a Scheme boolean. */
|
||||
return scm_from_bool (guile_shape->c_shape &&
|
||||
(guile_shape->c_shape->type == DIA_SQUARE));
|
||||
@}
|
||||
#undef FUNC_NAME
|
||||
@end lisp
|
||||
|
||||
Notice how easy it is to chain through from the @code{SCM shape}
|
||||
parameter that @code{square_p} receives --- which is a SMOB --- to the
|
||||
Scheme-specific structure inside the SMOB, and thence to the underlying
|
||||
C structure for the shape.
|
||||
|
||||
In this code, @code{SCM_SMOB_DATA} and @code{scm_from_bool} are from
|
||||
the standard Guile API. @code{SCM_VALIDATE_SHAPE} is a macro that you
|
||||
should define as part of your SMOB definition: it checks that the
|
||||
passed parameter is of the expected type. This is needed to guard
|
||||
against Scheme code using the @code{square?} procedure incorrectly, as
|
||||
in @code{(square? "hello")}; Scheme's latent typing means that usage
|
||||
errors like this must be caught at run time.
|
||||
|
||||
Having written the C code for your primitives, you need to make them
|
||||
available as Scheme procedures by calling the @code{scm_c_define_gsubr}
|
||||
function. @code{scm_c_define_gsubr} (REFFIXME) takes arguments that
|
||||
specify the Scheme-level name for the primitive and how many required,
|
||||
optional and rest arguments it can accept. The @code{square?} primitive
|
||||
always requires exactly one argument, so the call to make it available
|
||||
in Scheme reads like this:
|
||||
|
||||
@lisp
|
||||
scm_c_define_gsubr ("square?", 1, 0, 0, square_p);
|
||||
@end lisp
|
||||
|
||||
For where to put this call, see the subsection after next on the
|
||||
structure of Guile-enabled code (@pxref{Dia Structure}).
|
||||
|
||||
|
||||
@node Dia Hook
|
||||
@subsubsection Providing a Hook for the Evaluation of Scheme Code
|
||||
|
||||
To make the Guile integration useful, you have to design some kind of
|
||||
hook into your application that application users can use to cause their
|
||||
Scheme code to be evaluated.
|
||||
|
||||
Technically, this is straightforward; you just have to decide on a
|
||||
mechanism that is appropriate for your application. Think of Emacs, for
|
||||
example: when you type @kbd{@key{ESC} :}, you get a prompt where you can
|
||||
type in any Elisp code, which Emacs will then evaluate. Or, again like
|
||||
Emacs, you could provide a mechanism (such as an init file) to allow
|
||||
Scheme code to be associated with a particular key sequence, and
|
||||
evaluate the code when that key sequence is entered.
|
||||
|
||||
In either case, once you have the Scheme code that you want to evaluate,
|
||||
as a null terminated string, you can tell Guile to evaluate it by
|
||||
calling the @code{scm_c_eval_string} function.
|
||||
|
||||
|
||||
@node Dia Structure
|
||||
@subsubsection Top-level Structure of Guile-enabled Dia
|
||||
|
||||
Let's assume that the pre-Guile Dia code looks structurally like this:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
@code{main ()}
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
do lots of initialization and setup stuff
|
||||
@item
|
||||
enter Gtk main loop
|
||||
@end itemize
|
||||
@end itemize
|
||||
|
||||
When you add Guile to a program, one (rather technical) requirement is
|
||||
that Guile's garbage collector needs to know where the bottom of the C
|
||||
stack is. The easiest way to ensure this is to use
|
||||
@code{scm_boot_guile} like this:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
@code{main ()}
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
do lots of initialization and setup stuff
|
||||
@item
|
||||
@code{scm_boot_guile (argc, argv, inner_main, NULL)}
|
||||
@end itemize
|
||||
|
||||
@item
|
||||
@code{inner_main ()}
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
define all SMOB types
|
||||
@item
|
||||
export primitives to Scheme using @code{scm_c_define_gsubr}
|
||||
@item
|
||||
enter Gtk main loop
|
||||
@end itemize
|
||||
@end itemize
|
||||
|
||||
In other words, you move the guts of what was previously in your
|
||||
@code{main} function into a new function called @code{inner_main}, and
|
||||
then add a @code{scm_boot_guile} call, with @code{inner_main} as a
|
||||
parameter, to the end of @code{main}.
|
||||
|
||||
Assuming that you are using SMOBs and have written primitive code as
|
||||
described in the preceding subsections, you also need to insert calls to
|
||||
declare your new SMOBs and export the primitives to Scheme. These
|
||||
declarations must happen @emph{inside} the dynamic scope of the
|
||||
@code{scm_boot_guile} call, but also @emph{before} any code is run that
|
||||
could possibly use them --- the beginning of @code{inner_main} is an
|
||||
ideal place for this.
|
||||
|
||||
|
||||
@node Dia Advanced
|
||||
@subsubsection Going Further with Dia and Guile
|
||||
|
||||
The steps described so far implement an initial Guile integration that
|
||||
already gives a lot of additional power to Dia application users. But
|
||||
there are further steps that you could take, and it's interesting to
|
||||
consider a few of these.
|
||||
|
||||
In general, you could progressively move more of Dia's source code from
|
||||
C into Scheme. This might make the code more maintainable and
|
||||
extensible, and it could open the door to new programming paradigms that
|
||||
are tricky to effect in C but straightforward in Scheme.
|
||||
|
||||
A specific example of this is that you could use the guile-gtk package,
|
||||
which provides Scheme-level procedures for most of the Gtk+ library, to
|
||||
move the code that lays out and displays Dia objects from C to Scheme.
|
||||
|
||||
As you follow this path, it naturally becomes less useful to maintain a
|
||||
distinction between Dia's original non-Guile-related source code, and
|
||||
its later code implementing SMOBs and primitives for the Scheme world.
|
||||
|
||||
For example, suppose that the original source code had a
|
||||
@code{dia_change_fill_pattern} function:
|
||||
|
||||
@lisp
|
||||
void dia_change_fill_pattern (struct dia_shape * shape,
|
||||
struct dia_pattern * pattern)
|
||||
@{
|
||||
/* real pattern change work */
|
||||
@}
|
||||
@end lisp
|
||||
|
||||
During initial Guile integration, you add a @code{change_fill_pattern}
|
||||
primitive for Scheme purposes, which accesses the underlying structures
|
||||
from its SMOB values and uses @code{dia_change_fill_pattern} to do the
|
||||
real work:
|
||||
|
||||
@lisp
|
||||
SCM change_fill_pattern (SCM shape, SCM pattern)
|
||||
@{
|
||||
struct dia_shape * d_shape;
|
||||
struct dia_pattern * d_pattern;
|
||||
|
||||
@dots{}
|
||||
|
||||
dia_change_fill_pattern (d_shape, d_pattern);
|
||||
|
||||
return SCM_UNSPECIFIED;
|
||||
@}
|
||||
@end lisp
|
||||
|
||||
At this point, it makes sense to keep @code{dia_change_fill_pattern} and
|
||||
@code{change_fill_pattern} separate, because
|
||||
@code{dia_change_fill_pattern} can also be called without going through
|
||||
Scheme at all, say because the user clicks a button which causes a
|
||||
C-registered Gtk+ callback to be called.
|
||||
|
||||
But, if the code for creating buttons and registering their callbacks is
|
||||
moved into Scheme (using guile-gtk), it may become true that
|
||||
@code{dia_change_fill_pattern} can no longer be called other than
|
||||
through Scheme. In which case, it makes sense to abolish it and move
|
||||
its contents directly into @code{change_fill_pattern}, like this:
|
||||
|
||||
@lisp
|
||||
SCM change_fill_pattern (SCM shape, SCM pattern)
|
||||
@{
|
||||
struct dia_shape * d_shape;
|
||||
struct dia_pattern * d_pattern;
|
||||
|
||||
@dots{}
|
||||
|
||||
/* real pattern change work */
|
||||
|
||||
return SCM_UNSPECIFIED;
|
||||
@}
|
||||
@end lisp
|
||||
|
||||
So further Guile integration progressively @emph{reduces} the amount of
|
||||
functional C code that you have to maintain over the long term.
|
||||
|
||||
A similar argument applies to data representation. In the discussion of
|
||||
SMOBs earlier, issues arose because of the different memory management
|
||||
and lifetime models that normally apply to data structures in C and in
|
||||
Scheme. However, with further Guile integration, you can resolve this
|
||||
issue in a more radical way by allowing all your data structures to be
|
||||
under the control of the garbage collector, and kept alive by references
|
||||
from the Scheme world. Instead of maintaining an array or linked list
|
||||
of shapes in C, you would instead maintain a list in Scheme.
|
||||
|
||||
Rather like the coalescing of @code{dia_change_fill_pattern} and
|
||||
@code{change_fill_pattern}, the practical upshot of such a change is
|
||||
that you would no longer have to keep the @code{dia_shape} and
|
||||
@code{dia_guile_shape} structures separate, and so wouldn't need to
|
||||
worry about the pointers between them. Instead, you could change the
|
||||
SMOB definition to wrap the @code{dia_shape} structure directly, and
|
||||
send @code{dia_guile_shape} off to the scrap yard. Cut out the middle
|
||||
man!
|
||||
|
||||
Finally, we come to the holy grail of Guile's free software / extension
|
||||
language approach. Once you have a Scheme representation for
|
||||
interesting Dia data types like shapes, and a handy bunch of primitives
|
||||
for manipulating them, it suddenly becomes clear that you have a bundle
|
||||
of functionality that could have far-ranging use beyond Dia itself. In
|
||||
other words, the data types and primitives could now become a library,
|
||||
and Dia becomes just one of the many possible applications using that
|
||||
library --- albeit, at this early stage, a rather important one!
|
||||
|
||||
In this model, Guile becomes just the glue that binds everything
|
||||
together. Imagine an application that usefully combined functionality
|
||||
from Dia, Gnumeric and GnuCash --- it's tricky right now, because no
|
||||
such application yet exists; but it'll happen some day @dots{}
|
||||
|
||||
|
||||
@node Scheme vs C
|
||||
@subsection Why Scheme is More Hackable Than C
|
||||
|
||||
Underlying Guile's value proposition is the assumption that programming
|
||||
in a high level language, specifically Guile's implementation of Scheme,
|
||||
is necessarily better in some way than programming in C. What do we
|
||||
mean by this claim, and how can we be so sure?
|
||||
|
||||
One class of advantages applies not only to Scheme, but more generally
|
||||
to any interpretable, high level, scripting language, such as Emacs
|
||||
Lisp, Python, Ruby, or @TeX{}'s macro language. Common features of all
|
||||
such languages, when compared to C, are that:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
They lend themselves to rapid and experimental development cycles,
|
||||
owing usually to a combination of their interpretability and the
|
||||
integrated development environment in which they are used.
|
||||
|
||||
@item
|
||||
They free developers from some of the low level bookkeeping tasks
|
||||
associated with C programming, notably memory management.
|
||||
|
||||
@item
|
||||
They provide high level features such as container objects and exception
|
||||
handling that make common programming tasks easier.
|
||||
@end itemize
|
||||
|
||||
In the case of Scheme, particular features that make programming easier
|
||||
--- and more fun! --- are its powerful mechanisms for abstracting parts
|
||||
of programs (closures --- @pxref{About Closure}) and for iteration
|
||||
(@pxref{while do}).
|
||||
|
||||
The evidence in support of this argument is empirical: the huge amount
|
||||
of code that has been written in extension languages for applications
|
||||
that support this mechanism. Most notable are extensions written in
|
||||
Emacs Lisp for GNU Emacs, in @TeX{}'s macro language for @TeX{}, and in
|
||||
Script-Fu for the Gimp, but there is increasingly now a significant code
|
||||
eco-system for Guile-based applications as well, such as Lilypond and
|
||||
GnuCash. It is close to inconceivable that similar amounts of
|
||||
functionality could have been added to these applications just by
|
||||
writing new code in their base implementation languages.
|
||||
|
||||
|
||||
@node Testbed Example
|
||||
@subsection Example: Using Guile for an Application Testbed
|
||||
|
||||
As an example of what this means in practice, imagine writing a testbed
|
||||
for an application that is tested by submitting various requests (via a
|
||||
C interface) and validating the output received. Suppose further that
|
||||
the application keeps an idea of its current state, and that the
|
||||
``correct'' output for a given request may depend on the current
|
||||
application state. A complete ``white box''@footnote{A @dfn{white box}
|
||||
test plan is one that incorporates knowledge of the internal design of
|
||||
the application under test.} test plan for this application would aim to
|
||||
submit all possible requests in each distinguishable state, and validate
|
||||
the output for all request/state combinations.
|
||||
|
||||
To write all this test code in C would be very tedious. Suppose instead
|
||||
that the testbed code adds a single new C function, to submit an
|
||||
arbitrary request and return the response, and then uses Guile to export
|
||||
this function as a Scheme procedure. The rest of the testbed can then
|
||||
be written in Scheme, and so benefits from all the advantages of
|
||||
programming in Scheme that were described in the previous section.
|
||||
|
||||
(In this particular example, there is an additional benefit of writing
|
||||
most of the testbed in Scheme. A common problem for white box testing
|
||||
is that mistakes and mistaken assumptions in the application under test
|
||||
can easily be reproduced in the testbed code. It is more difficult to
|
||||
copy mistakes like this when the testbed is written in a different
|
||||
language from the application.)
|
||||
|
||||
|
||||
@node Programming Options
|
||||
@subsection A Choice of Programming Options
|
||||
|
||||
The preceding arguments and example point to a model of Guile
|
||||
programming that is applicable in many cases. According to this model,
|
||||
Guile programming involves a balance between C and Scheme programming,
|
||||
with the aim being to extract the greatest possible Scheme level benefit
|
||||
from the least amount of C level work.
|
||||
|
||||
The C level work required in this model usually consists of packaging
|
||||
and exporting functions and application objects such that they can be
|
||||
seen and manipulated on the Scheme level. To help with this, Guile's C
|
||||
language interface includes utility features that aim to make this kind
|
||||
of integration very easy for the application developer. These features
|
||||
are documented later in this part of the manual: see REFFIXME.
|
||||
|
||||
This model, though, is really just one of a range of possible
|
||||
programming options. If all of the functionality that you need is
|
||||
available from Scheme, you could choose instead to write your whole
|
||||
application in Scheme (or one of the other high level languages that
|
||||
Guile supports through translation), and simply use Guile as an
|
||||
interpreter for Scheme. (In the future, we hope that Guile will also be
|
||||
able to compile Scheme code, so lessening the performance gap between C
|
||||
and Scheme code.) Or, at the other end of the C--Scheme scale, you
|
||||
could write the majority of your application in C, and only call out to
|
||||
Guile occasionally for specific actions such as reading a configuration
|
||||
file or executing a user-specified extension. The choices boil down to
|
||||
two basic questions:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
Which parts of the application do you write in C, and which in Scheme
|
||||
(or another high level translated language)?
|
||||
|
||||
@item
|
||||
How do you design the interface between the C and Scheme parts of your
|
||||
application?
|
||||
@end itemize
|
||||
|
||||
These are of course design questions, and the right design for any given
|
||||
application will always depend upon the particular requirements that you
|
||||
are trying to meet. In the context of Guile, however, there are some
|
||||
generally applicable considerations that can help you when designing
|
||||
your answers.
|
||||
|
||||
@menu
|
||||
* Available Functionality:: What functionality is already available?
|
||||
* Basic Constraints:: Functional and performance constraints.
|
||||
* Style Choices:: Your preferred programming style.
|
||||
* Program Control:: What controls program execution?
|
||||
@end menu
|
||||
|
||||
|
||||
@node Available Functionality
|
||||
@subsubsection What Functionality is Already Available?
|
||||
|
||||
Suppose, for the sake of argument, that you would prefer to write your
|
||||
whole application in Scheme. Then the API available to you consists of:
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
standard Scheme
|
||||
|
||||
@item
|
||||
plus the extensions to standard Scheme provided by
|
||||
Guile in its core distribution
|
||||
|
||||
@item
|
||||
plus any additional functionality that you or others have packaged so
|
||||
that it can be loaded as a Guile Scheme module.
|
||||
@end itemize
|
||||
|
||||
A module in the last category can either be a pure Scheme module --- in
|
||||
other words a collection of utility procedures coded in Scheme --- or a
|
||||
module that provides a Scheme interface to an extension library coded in
|
||||
C --- in other words a nice package where someone else has done the work
|
||||
of wrapping up some useful C code for you. The set of available modules
|
||||
is growing quickly and already includes such useful examples as
|
||||
@code{(gtk gtk)}, which makes Gtk+ drawing functions available in
|
||||
Scheme, and @code{(database postgres)}, which provides SQL access to a
|
||||
Postgres database.
|
||||
|
||||
Given the growing collection of pre-existing modules, it is quite
|
||||
feasible that your application could be implemented by combining a
|
||||
selection of these modules together with new application code written in
|
||||
Scheme.
|
||||
|
||||
If this approach is not enough, because the functionality that your
|
||||
application needs is not already available in this form, and it is
|
||||
impossible to write the new functionality in Scheme, you will need to
|
||||
write some C code. If the required function is already available in C
|
||||
(e.g. in a library), all you need is a little glue to connect it to the
|
||||
world of Guile. If not, you need both to write the basic code and to
|
||||
plumb it into Guile.
|
||||
|
||||
In either case, two general considerations are important. Firstly, what
|
||||
is the interface by which the functionality is presented to the Scheme
|
||||
world? Does the interface consist only of function calls (for example,
|
||||
a simple drawing interface), or does it need to include @dfn{objects} of
|
||||
some kind that can be passed between C and Scheme and manipulated by
|
||||
both worlds. Secondly, how does the lifetime and memory management of
|
||||
objects in the C code relate to the garbage collection governed approach
|
||||
of Scheme objects? In the case where the basic C code is not already
|
||||
written, most of the difficulties of memory management can be avoided by
|
||||
using Guile's C interface features from the start.
|
||||
|
||||
For the full documentation on writing C code for Guile and connecting
|
||||
existing C code to the Guile world, see REFFIXME.
|
||||
|
||||
|
||||
@node Basic Constraints
|
||||
@subsubsection Functional and Performance Constraints
|
||||
|
||||
|
||||
@node Style Choices
|
||||
@subsubsection Your Preferred Programming Style
|
||||
|
||||
|
||||
@node Program Control
|
||||
@subsubsection What Controls Program Execution?
|
||||
|
||||
|
||||
@node User Programming
|
||||
@subsection How About Application Users?
|
||||
|
||||
So far we have considered what Guile programming means for an
|
||||
application developer. But what if you are instead @emph{using} an
|
||||
existing Guile-based application, and want to know what your
|
||||
options are for programming and extending this application?
|
||||
|
||||
The answer to this question varies from one application to another,
|
||||
because the options available depend inevitably on whether the
|
||||
application developer has provided any hooks for you to hang your own
|
||||
code on and, if there are such hooks, what they allow you to
|
||||
do.@footnote{Of course, in the world of free software, you always have
|
||||
the freedom to modify the application's source code to your own
|
||||
requirements. Here we are concerned with the extension options that the
|
||||
application has provided for without your needing to modify its source
|
||||
code.} For example@dots{}
|
||||
|
||||
@itemize @bullet
|
||||
@item
|
||||
If the application permits you to load and execute any Guile code, the
|
||||
world is your oyster. You can extend the application in any way that
|
||||
you choose.
|
||||
|
||||
@item
|
||||
A more cautious application might allow you to load and execute Guile
|
||||
code, but only in a @dfn{safe} environment, where the interface
|
||||
available is restricted by the application from the standard Guile API.
|
||||
|
||||
@item
|
||||
Or a really fearful application might not provide a hook to really
|
||||
execute user code at all, but just use Scheme syntax as a convenient way
|
||||
for users to specify application data or configuration options.
|
||||
@end itemize
|
||||
|
||||
In the last two cases, what you can do is, by definition, restricted by
|
||||
the application, and you should refer to the application's own manual to
|
||||
find out your options.
|
||||
|
||||
The most well known example of the first case is Emacs, with its
|
||||
extension language Emacs Lisp: as well as being a text editor, Emacs
|
||||
supports the loading and execution of arbitrary Emacs Lisp code. The
|
||||
result of such openness has been dramatic: Emacs now benefits from
|
||||
user-contributed Emacs Lisp libraries that extend the basic editing
|
||||
function to do everything from reading news to psychoanalysis and
|
||||
playing adventure games. The only limitation is that extensions are
|
||||
restricted to the functionality provided by Emacs's built-in set of
|
||||
primitive operations. For example, you can interact and display data by
|
||||
manipulating the contents of an Emacs buffer, but you can't pop-up and
|
||||
draw a window with a layout that is totally different to the Emacs
|
||||
standard.
|
||||
|
||||
This situation with a Guile application that supports the loading of
|
||||
arbitrary user code is similar, except perhaps even more so, because
|
||||
Guile also supports the loading of extension libraries written in C.
|
||||
This last point enables user code to add new primitive operations to
|
||||
Guile, and so to bypass the limitation present in Emacs Lisp.
|
||||
|
||||
At this point, the distinction between an application developer and an
|
||||
application user becomes rather blurred. Instead of seeing yourself as
|
||||
a user extending an application, you could equally well say that you are
|
||||
developing a new application of your own using some of the primitive
|
||||
functionality provided by the original application. As such, all the
|
||||
discussions of the preceding sections of this chapter are relevant to
|
||||
how you can proceed with developing your extension.
|
Loading…
Add table
Add a link
Reference in a new issue