.. _library_hook_objects:

``hook_objects``
================

This library provides a set of convenient hook objects for defining
custom expansion workflows (using, e.g., the ``hook_flows`` library) and
for debugging. They are usable and useful as-is but should also be
regarded as term- and goal-expansion examples that you can learn from,
clone, and change to fit your application requirements.

API documentation
-----------------

Open the
`../../apis/library_index.html#hook-objects <../../apis/library_index.html#hook-objects>`__
link in a web browser.

Loading
-------

To load all hook objects in this library, load the ``loader.lgt`` file:

::

   | ?- logtalk_load(hook_objects(loader)).

To load a specific hook object, e.g., the ``backend_adapter_hook``
object:

::

   | ?- logtalk_load(hook_objects(backend_adapter_hook)).

Testing
-------

To test this library hook objects, load the ``tester.lgt`` file:

::

   | ?- logtalk_load(hook_objects(tester)).

Usage
-----

The provided hook objects cover different expansion scenarios as
follows.

Using the Prolog backend adapter file expansion rules
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Useful when defining a custom expansion workflow. This can be
accomplished by loading the ``backend_adapter_hook.lgt`` file, which
defines a ``backend_adapter_hook`` hook object that can be used as a
workflow step.

Restoring the default compiler expansion workflow
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In this case, load the ``default_workflow_hook.lgt`` file, which defines
a ``default_workflow_hook`` hook object, and use the following **goal**
to set the default hook object:

::

   | ?- set_logtalk_flag(hook, default_workflow_hook).

Preventing applying any (other) user-defined expansion rules
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

When compiling a source file, we sometimes want to prevent applying
expansion rules. This can be accomplished by simply loading the
``identity_hook.lgt`` file, which defines the ``identity_hook`` hook
object, whose expansion rules simply succeed without changing the terms
and goals, and setting it as the file-specific hook object writing as
the first term in the file the **directive**:

::

   :- set_logtalk_flag(hook, identity_hook).

Note that the compiler will always convert any grammar rules defined in
the file into clauses. Although this conversion can also be performed as
an expansion, grammar rules are part of the Logtalk language. If you
want to preserve the grammar rules, use the hook objects described below
to write them to a stream.

Expanding grammar rules into clauses independently of the compiler
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Load the ``grammar_rules_hook.lgt`` and use the term-expansion rules in
the ``grammar_rules_hook`` object. For example:

::

   | ?- grammar_rules_hook::term_expansion((a --> [b],c), Clause).

   Clause = (a([b|T], C) :- c(T, C))
   yes

Using the expansion rules defined in a Prolog module
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Load the ``prolog_module_hook.lgt``, which defines the parametric hook
object ``prolog_module_hook(Module)``. To use this hook object, you need
to instantiate the parameter to the name of the module. For example:

::

   :- set_logtalk_flag(hook, prolog_module_hook(user)).

Wrap the contents of a plain Prolog file as an object
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Load the ``object_wrapper_hook.lgt``, which defines the
``object_wrapper_hook/0-2`` hook objects. Use them to wrap the contents
of a plain Prolog file as an object named after the file (optionally
implementing a protocol) or an object with the given name and object
relations. Can be used to apply Logtalk developer tools to plain Prolog
code or when porting a Prolog application to Logtalk. For example:

::

   | ?- logtalk_load('plain.pl', [hook(object_wrapper_hook)]).
   ...

   | ?- current_object(plain).
   yes

Or:

::

   | ?- logtalk_load('world_1.pl', [hook(object_wrapper_hook(some_protocol))]).
   ...

   | ?- current_object(world_1).
   yes

   | ?- implements_protocol(world_1, Protocol).
   Protocol = some_protocol
   yes

Or:

::

   | ?- logtalk_load('foo.pl', [hook(object_wrapper_hook(bar,[imports(some_category))]).
   ...

   | ?- current_object(bar).
   yes

   | ?- imports_category(bar, Category).
   Category = some_category
   yes

The ``object_wrapper_hook`` object sets the ``context_switching_calls``
flag to ``allow`` for the generated object. This enables calling the
predicates using the ``(<<)/2`` context-switching control construct. But
it's usually better to define a protocol for the predicates being
encapsulated and use instead the ``object_wrapper_hook/1-2`` objects.

Outputting term-expansion results to a stream or a file
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Load the ``write_to_stream_hook.lgt`` file and use the
``write_to_stream_hook(Stream)`` or
``write_to_stream_hook(Stream, Options)`` hook objects. Alternatively,
you can load the ``write_to_file_hook.lgt`` file and use the
``write_to_file_hook(File)`` or ``write_to_file_hook(File, Options)``
hook objects. The terms are not modified and thus these hook objects may
be used at any point in an expansion workflow. The terms are written
followed by a period and a new line.

For example, assume that we want to expand all terms in a ``input.pl``
source file, writing the resulting terms to a ``output.pl`` file, using
the expansion rules defined in a ``expansions`` hook object. Taking
advantage of the ``hook_flows`` library ``hook_pipeline/1`` object, we
can write:

::

   | ?- logtalk_compile(
            'input.pl',
            [hook(hook_pipeline([
                expansions,
                write_to_file_hook('output.pl')
            ]))]
        ).

Printing entity predicate goals before or after calling them
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This is helpful for quick debugging. Load the ``print_goal_hook.lgt``
file and use the ``print_goal_hook`` hook object. For example, we can
set this hook object as the default hook:

::

   | ?- set_logtalk_flag(hook, print_goal_hook).

Then, edit the entity source code to print selected goals:

::

   foo :-
       - bar,   % print goal before calling it
       + baz,   % print goal after calling it
       * quux.  % print goal before and after calling it

Suppressing goals
~~~~~~~~~~~~~~~~~

The ``suppress_goal_hook.lgt`` file provides the ``suppress_goal_hook``
hook object that supports suppressing a goal in a clause body by
prefixing it using the ``--`` operator. We can set this hook object as
the default hook using the goal:

::

   | ?- set_logtalk_flag(hook, suppress_goal_hook).

If the expansion is only to be used in a single file, use instead the
source file directive:

::

   :- set_logtalk_flag(hook, suppress_goal_hook).

Then, edit entity predicates to suppress goals. For example:

::

   foo :-
       bar,
       -- baz,
       quux.

The suppressed goals are replaced by calls to ``true/0``.
