/[guile]/guile/guile-core/doc/ref/program.texi
ViewVC logotype

Diff of /guile/guile-core/doc/ref/program.texi

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.5.2.2 by ossau, Sat Apr 13 09:03:47 2002 UTC revision 1.5.2.3 by ossau, Sat Apr 20 11:02:37 2002 UTC
# Line 31  available. Line 31  available.
31  @node Extending Dia  @node Extending Dia
32  @section How One Might Extend Dia Using Guile  @section How One Might Extend Dia Using Guile
33    
34    Dia is a free software program for drawing schematic diagrams like flow
35    charts and floor plans (REFFIXME).  This section conducts the thought
36    experiment of adding Guile to Dia.  In so doing, it aims to illustrate
37    several of the steps and considerations involved in adding Guile to
38    applications in general.
39    
40    @menu
41    * Dia Objective::               Deciding why you want to add Guile.
42    * Dia Steps::                   Four steps required to add Guile.
43    * Dia Smobs::                   How to represent Dia data in Scheme.
44    * Dia Primitives::              Writing Guile primitives for Dia.
45    * Dia Hook::                    Providing a hook for Scheme evaluation.
46    * Dia Structure::               Overall structure for adding Guile.
47    * Dia Advanced::                Going further with Dia and Guile.
48    @end menu
49    
50    
51    @node Dia Objective
52    @subsection Deciding Why You Want to Add Guile
53    
54    First off, you should understand why you want to add Guile to Dia at
55    all, and that means forming a picture of what Dia does and how it does
56    it.  So, what are the constituents of the Dia application?
57    
58    @itemize @bullet
59    @item
60    Most importantly, the @dfn{application domain objects} --- in other
61    words, the concepts that differentiate Dia from another application such
62    as a word processor or spreadsheet: shapes, templates, connectors,
63    pages, plus the properties of all these things.
64    
65    @item
66    The code that manages the graphical face of the application, including
67    the layout and display of the objects above.
68    
69    @item
70    The code that handles input events, which indicate that the application
71    user is wanting to do something.
72    @end itemize
73    
74    @noindent
75    (In other words, a textbook example of the @dfn{model - view -
76    controller} paradigm.)
77    
78    Next question: how will Dia benefit once the Guile integration is
79    complete?  Several (positive!) answers are possible here, and the choice
80    is obviously up to the application developers.  Still, one answer is
81    that the main benefit will be the ability to manipulate Dia's
82    application domain objects from Scheme.
83    
84    Suppose that Dia made a set of procedures available in Scheme,
85    representing the most basic operations on objects such as shapes,
86    connectors, and so on.  Using Scheme, the application user could then
87    write code that builds upon these basic operations to create more
88    complex procedures.  For example, given basic procedures to enumerate
89    the objects on a page, to determine whether an object is a square, and
90    to change the fill pattern of a single shape, the user can write a
91    Scheme procedure to change the fill pattern of all squares on the
92    current page:
93    
94    @lisp
95    (define (change-squares'-fill-pattern new-pattern)
96      (for-each-shape current-page
97        (lambda (shape)
98          (if (square? shape)
99              (change-fill-pattern shape new-pattern)))))
100    @end lisp
101    
102    
103    @node Dia Steps
104    @subsection Four Steps Required to Add Guile
105    
106    Assuming this objective, four steps are needed to achieve it.
107    
108    First, you need a way of representing your application-specific objects
109    --- such as @code{shape} in the previous example --- when they are
110    passed into the Scheme world.  Unless your objects are so simple that
111    they map naturally into builtin Scheme data types like numbers and
112    strings, you will probably want to use Guile's @dfn{SMOB} interface to
113    create a new Scheme data type for your objects.
114    
115    Second, you need to write code for the basic operations like
116    @code{for-each-shape} and @code{square?} such that they access and
117    manipulate your existing data structures correctly, and then make these
118    operations available as @dfn{primitives} on the Scheme level.
119    
120    Third, you need to provide some mechanism within the Dia application
121    that a user can hook into to cause arbitrary Scheme code to be
122    evaluated.
123    
124    Finally, you need to restructure your top-level application C code a
125    little so that it initializes the Guile interpreter correctly and
126    declares your @dfn{SMOBs} and @dfn{primitives} to the Scheme world.
127    
128    The following subsections expand on these four points in turn.
129    
130    
131    @node Dia Smobs
132    @subsection How to Represent Dia Data in Scheme
133    
134    For all but the most trivial applications, you will probably want to
135    allow some representation of your domain objects to exist on the Scheme
136    level.  This is where the idea of SMOBs comes in, and with it issues of
137    lifetime management and garbage collection.
138    
139    To get more concrete about this, let's look again at the example we gave
140    earlier of how application users can use Guile to build higher-level
141    functions from the primitives that Dia itself provides.
142    
143    @lisp
144    (define (change-squares'-fill-pattern new-pattern)
145      (for-each-shape current-page
146        (lambda (shape)
147          (if (square? shape)
148              (change-fill-pattern shape new-pattern)))))
149    @end lisp
150    
151    Consider what is stored here in the variable @code{shape}.  For each
152    shape on the current page, the @code{for-each-shape} primitive calls
153    @code{(lambda (shape) @dots{})} with an argument representing that
154    shape.  Question is: how is that argument represented on the Scheme
155    level?  The issues are as follows.
156    
157    @itemize @bullet
158    @item
159    Whatever the representation, it has to be decodable again by the C code
160    for the @code{square?} and @code{change-fill-pattern} primitives.  In
161    other words, a primitive like @code{square?} has somehow to be able to
162    turn the value that it receives back into something that points to the
163    underlying C structure describing a shape.
164    
165    @item
166    The representation must also cope with Scheme code holding on to the
167    value for later use.  What happens if the Scheme code stores
168    @code{shape} in a global variable, but then that shape is deleted (in a
169    way that the Scheme code is not aware of), and later on some other
170    Scheme code uses that global variable again in a call to, say,
171    @code{square?}?
172    
173    @item
174    The lifetime and memory allocation of objects that exist @emph{only} in
175    the Scheme world is managed automatically by Guile's garbage collector
176    using one simple rule: when there are no remaining references to an
177    object, the object is considered dead and so its memory is freed.  But
178    for objects that exist in both C and Scheme, the picture is more
179    complicated; in the case of Dia, where the @code{shape} argument passes
180    transiently in and out of the Scheme world, it would be quite wrong the
181    @strong{delete} the underlying C shape just because the Scheme code has
182    finished evaluation.  How do we avoid this happening?
183    @end itemize
184    
185    One resolution of these issues is for the Scheme-level representation of
186    a shape to be a new, Scheme-specific C structure wrapped up as a SMOB.
187    The SMOB is what is passed into and out of Scheme code, and the
188    Scheme-specific C structure inside the SMOB points to Dia's underlying C
189    structure so that the code for primitives like @code{square?} can get at
190    it.
191    
192    To cope with an underlying shape being deleted while Scheme code is
193    still holding onto a Scheme shape value, the underlying C structure
194    should have a new field that points to the Scheme-specific SMOB.  When a
195    shape is deleted, the relevant code chains through to the
196    Scheme-specific structure and sets its pointer back to the underlying
197    structure to NULL.  Thus the SMOB value for the shape continues to
198    exist, but any primitive code that tries to use it will detect that the
199    underlying shape has been deleted because the underlying structure
200    pointer is NULL.
201    
202    So, to summarize the steps involved in this resolution of the problem
203    (and assuming that the underlying C structure for a shape is
204    @code{struct dia_shape}):
205    
206    @itemize @bullet
207    @item
208    Define a new Scheme-specific structure that @emph{points} to the
209    underlying C structure:
210    
211    @lisp
212    struct dia_guile_shape
213    @{
214      struct dia_shape * c_shape;   /* NULL => deleted */
215    @}
216    @end lisp
217    
218    @item
219    Add a field to @code{struct dia_shape} that points to its @code{struct
220    dia_guile_shape} if it has one ---
221    
222    @lisp
223    struct dia_shape
224    @{
225      @dots{}
226      struct dia_guile_shape * guile_shape;
227    @}
228    @end lisp
229    
230    @noindent
231    --- so that C code can set @code{guile_shape->c_shape} to NULL when the
232    underlying shape is deleted.
233    
234    @item
235    Wrap @code{struct dia_guile_shape} as a SMOB type.
236    
237    @item
238    Whenever you need to represent a C shape onto the Scheme level, create a
239    SMOB instance for it, and pass that.
240    
241    @item
242    In primitive code that receives a shape SMOB instance, check the
243    @code{c_shape} field when decoding it, to find out whether the
244    underlying C shape is still there.
245    @end itemize
246    
247    As far as memory management is concerned, the SMOB values and their
248    Scheme-specific structures are under the control of the garbage
249    collector, whereas the underlying C structures are explicitly managed in
250    exactly the same way that Dia managed them before we thought of adding
251    Guile.
252    
253    When the garbage collector decides to free a shape SMOB value, it calls
254    the @dfn{SMOB free} function that was specified when defining the shape
255    SMOB type.  To maintain the correctness of the @code{guile_shape} field
256    in the underlying C structure, this function should chain through to the
257    underlying C structure (if it still exists) and set its
258    @code{guile_shape} field to NULL.
259    
260    For full documentation on defining and using SMOB types, see
261    @ref{Defining New Types (Smobs)}.
262    
263    
264    @node Dia Primitives
265    @subsection Writing Guile Primitives for Dia
266    
267    Once the details of object representation are decided, writing the
268    primitive function code that you need is usually straightforward.
269    
270    A primitive is simply a C function whose arguments and return value are
271    all of type @code{SCM}, and whose body does whatever you want it to do.
272    As an example, here is a possible implementation of the @code{square?}
273    primitive:
274    
275    @lisp
276    #define FUNC_NAME "square?"
277    static SCM square_p (SCM shape)
278    @{
279      struct dia_guile_shape * guile_shape;
280    
281      /* Check that arg is really a shape SMOB. */
282      SCM_VALIDATE_SHAPE (SCM_ARG1, shape);
283    
284      /* Access Scheme-specific shape structure. */
285      guile_shape = SCM_SMOB_DATA (shape);
286    
287      /* Find out if underlying shape exists and is a
288         square; return answer as a Scheme boolean. */
289      return SCM_BOOL (guile_shape->c_shape &&
290                       (guile_shape->c_shape->type == DIA_SQUARE));
291    @}
292    #undef FUNC_NAME
293    @end lisp
294    
295    Notice how easy it is to chain through from the @code{SCM shape}
296    parameter that @code{square_p} receives --- which is a SMOB --- to the
297    Scheme-specific structure inside the SMOB, and thence to the underlying
298    C structure for the shape.
299    
300    In this code, @code{SCM_SMOB_DATA} and @code{SCM_BOOL} are macros from
301    the standard Guile API.  @code{SCM_VALIDATE_SHAPE} is a macro that you
302    should define as part of your SMOB definition: it checks that the passed
303    parameter is of the expected type.  This is needed to guard against
304    Scheme code using the @code{square?} procedure incorrectly, as in
305    @code{(square? "hello")}; Scheme's latent typing means that usage errors
306    like this must be caught at run time.
307    
308    Having written the C code for your primitives, you need to make them
309    available as Scheme procedures by calling the @code{scm_c_define_gsubr}
310    function.  @code{scm_c_define_gsubr} (REFFIXME) takes arguments that
311    specify the Scheme-level name for the primitive and how many required,
312    optional and rest arguments it can accept.  The @code{square?} primitive
313    always requires exactly one argument, so the call to make it available
314    in Scheme reads like this:
315    
316    @lisp
317    scm_c_define_gsubr ("square?", 1, 0, 0, square_p);
318    @end lisp
319    
320    For where to put this call, see the subsection after next on the
321    structure of Guile-enabled code (@pxref{Dia Structure}).
322    
323    
324    @node Dia Hook
325    @subsection Providing a Hook for the Evaluation of Scheme Code
326    
327    To make the Guile integration useful, you have to design some kind of
328    hook into your application that application users can use to cause their
329    Scheme code to be evaluated.
330    
331    Technically, this is straightforward; you just have to decide on a
332    mechanism that is appropriate for your application.  Think of Emacs, for
333    example: when you type @kbd{@key{ESC} :}, you get a prompt where you can
334    type in any Elisp code, which Emacs will then evaluate.  Or, again like
335    Emacs, you could provide a mechanism (such as an init file) to allow
336    Scheme code to be associated with a particular key sequence, and
337    evaluate the code when that key sequence is entered.
338    
339    In either case, once you have the Scheme code that you want to evaluate,
340    as a null terminated string, you can tell Guile to evaluate it by
341    calling the @code{scm_c_eval_string} function.
342    
343    
344    @node Dia Structure
345    @subsection Top-level Structure of Guile-enabled Dia
346    
347    Let's assume that the pre-Guile Dia code looks structurally like this:
348    
349    @itemize @bullet
350    @item
351    @code{main ()}
352    
353    @itemize @bullet
354    @item
355    do lots of initialization and setup stuff
356    @item
357    enter Gtk main loop
358    @end itemize
359    @end itemize
360    
361    When you add Guile to a program, one (rather technical) requirement is
362    that Guile's garbage collector needs to know where the bottom of the C
363    stack is.  The easiest way to ensure this is to use
364    @code{scm_boot_guile} like this:
365    
366    @itemize @bullet
367    @item
368    @code{main ()}
369    
370    @itemize @bullet
371    @item
372    do lots of initialization and setup stuff
373    @item
374    @code{scm_boot_guile (argc, argv, inner_main, NULL)}
375    @end itemize
376    
377    @item
378    @code{inner_main ()}
379    
380    @itemize @bullet
381    @item
382    define all SMOB types
383    @item
384    export primitives to Scheme using @code{scm_c_define_gsubr}
385    @item
386    enter Gtk main loop
387    @end itemize
388    @end itemize
389    
390    In other words, you move the guts of what was previously in your
391    @code{main} function into a new function called @code{inner_main}, and
392    then add a @code{scm_boot_guile} call, with @code{inner_main} as a
393    parameter, to the end of @code{main}.
394    
395    Assuming that you are using SMOBs and have written primitive code as
396    described in the preceding subsections, you also need to insert calls to
397    declare your new SMOBs and export the primitives to Scheme.  These
398    declarations must happen @emph{inside} the dynamic scope of the
399    @code{scm_boot_guile} call, but also @emph{before} any code is run that
400    could possibly use them --- the beginning of @code{inner_main} is an
401    ideal place for this.
402    
403    
404    @node Dia Advanced
405    @subsection Going Further with Dia and Guile
406    
407    The steps described so far implement an initial Guile integration that
408    already gives a lot of additional power to Dia application users.  But
409    there are further steps that you could take, and it's interesting to
410    consider a few of these.
411    
412    In general, you could progressively move more of Dia's source code from
413    C into Scheme.  This might make the code more maintainable and
414    extensible, and it could open the door to new programming paradigms that
415    are tricky to effect in C but straightforward in Scheme.
416    
417    A specific example of this is that you could use the guile-gtk package,
418    which provides Scheme-level procedures for most of the Gtk+ library, to
419    move the code that lays out and displays Dia objects from C to Scheme.
420    
421    As you follow this path, it naturally becomes less useful to maintain a
422    distinction between Dia's original non-Guile-related source code, and
423    its later code implementing SMOBs and primitives for the Scheme world.
424    
425    For example, suppose that the original source code had a
426    @code{dia_change_fill_pattern} function:
427    
428    @lisp
429    void dia_change_fill_pattern (struct dia_shape * shape,
430                                  struct dia_pattern * pattern)
431    @{
432      /* real pattern change work */
433    @}
434    @end lisp
435    
436    During initial Guile integration, you add a @code{change_fill_pattern}
437    primitive for Scheme purposes, which accesses the underlying structures
438    from its SMOB values and uses @code{dia_change_fill_pattern} to do the
439    real work:
440    
441    @lisp
442    SCM change_fill_pattern (SCM shape, SCM pattern)
443    @{
444      struct dia_shape * d_shape;
445      struct dia_pattern * d_pattern;
446    
447      @dots{}
448    
449      dia_change_fill_pattern (d_shape, d_pattern);
450    
451      return SCM_UNSPECIFIED;
452    @}
453    @end lisp
454    
455    At this point, it makes sense to keep @code{dia_change_fill_pattern} and
456    @code{change_fill_pattern} separate, because
457    @code{dia_change_fill_pattern} can also be called without going through
458    Scheme at all, say because the user clicks a button which causes a
459    C-registered Gtk+ callback to be called.
460    
461    But, if the code for creating buttons and registering their callbacks is
462    moved into Scheme (using guile-gtk), it may become true that
463    @code{dia_change_fill_pattern} can no longer be called other than
464    through Scheme.  In which case, it makes sense to abolish it and move
465    its contents directly into @code{change_fill_pattern}, like this:
466    
467    @lisp
468    SCM change_fill_pattern (SCM shape, SCM pattern)
469    @{
470      struct dia_shape * d_shape;
471      struct dia_pattern * d_pattern;
472    
473      @dots{}
474    
475      /* real pattern change work */
476    
477      return SCM_UNSPECIFIED;
478    @}
479    @end lisp
480    
481    So further Guile integration progressively @emph{reduces} the amount of
482    functional C code that you have to maintain over the long term.
483    
484    A similar argument applies to data representation.  In the discussion of
485    SMOBs earlier, issues arose because of the different memory management
486    and lifetime models that normally apply to data structures in C and in
487    Scheme.  However, with further Guile integration, you can resolve this
488    issue in a more radical way by allowing all your data structures to be
489    under the control of the garbage collector, and kept alive by references
490    from the Scheme world.  Instead of maintaining an array or linked list
491    of shapes in C, you would instead maintain a list in Scheme.
492    
493    Rather like the coalescing of @code{dia_change_fill_pattern} and
494    @code{change_fill_pattern}, the practical upshot of such a change is
495    that you would no longer have to keep the @code{dia_shape} and
496    @code{dia_guile_shape} structures separate, and so wouldn't need to
497    worry about the pointers between them.  Instead, you could change the
498    SMOB definition to wrap the @code{dia_shape} structure directly, and
499    send @code{dia_guile_shape} off to the scrap yard.  Cut out the middle
500    man!
501    
502    Finally, we come to the holy grail of Guile's free software / extension
503    language approach.  Once you have a Scheme representation for
504    interesting Dia data types like shapes, and a handy bunch of primitives
505    for manipulating them, it suddenly becomes clear that you have a bundle
506    of functionality that could have far-ranging use beyond Dia itself.  In
507    other words, the data types and primitives could now become a library,
508    and Dia becomes just one of the many possible applications using that
509    library --- albeit, at this early stage, a rather important one!
510    
511    In this model, Guile becomes just the glue that binds everything
512    together.  Imagine an application that usefully combined functionality
513    from Dia, Gnumeric and GnuCash --- it's tricky right now, because no
514    such application yet exists; but it'll happen some day @dots{}
515    
516    
517  @node Scheme vs C  @node Scheme vs C
518  @section Why Scheme is More Hackable Than C  @section Why Scheme is More Hackable Than C
519    
520  Underlying Guile's argument is the assumption that programming in a high  Underlying Guile's value proposition is the assumption that programming
521  level language, specifically Guile's implementation of Scheme, is  in a high level language, specifically Guile's implementation of Scheme,
522  necessarily better in some way than programming in C.  What do we mean  is necessarily better in some way than programming in C.  What do we
523  by this claim, and how can we be so sure?  mean by this claim, and how can we be so sure?
524    
525  One class of advantages applies not only to Scheme, but more generally  One class of advantages applies not only to Scheme, but more generally
526  to any interpretable, high level, scripting language, such as Emacs  to any interpretable, high level, scripting language, such as Emacs
# Line 60  They provide high level features such as Line 542  They provide high level features such as
542  handling that make common programming tasks easier.  handling that make common programming tasks easier.
543  @end itemize  @end itemize
544    
545  In the case of Scheme, further features that make programming easier ---  In the case of Scheme, particular features that make programming easier
546  and more fun! --- are its powerful mechanisms for abstracting parts of  --- and more fun! --- are its powerful mechanisms for abstracting parts
547  programs (closures --- @pxref{About Closure}) and for iteration  of programs (closures --- @pxref{About Closure}) and for iteration
548  (@pxref{while do}).  (@pxref{while do}).
549    
550  The evidence in support of this argument is empirical: the huge amount  The evidence in support of this argument is empirical: the huge amount

Legend:
Removed from v.1.5.2.2  
changed lines
  Added in v.1.5.2.3

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26