UnifyWeaver generates bash code using a template system rather than building strings with concatenation. This approach keeps code generation clean, maintainable, and readable.
Bad approach (concatenation):
% ❌ Hard to read and maintain
generate_function(Name, Body, Code) :-
format(string(Code), '#!/bin/bash\n~w() {\n~w\n}\n', [Name, Body]).
Good approach (templates):
% ✅ Clear, readable, maintainable
Template = '#!/bin/bash\n() {\n\n}',
render_template(Template, [name=Name, body=Body], Code).
The template system separates the structure of the code (the template) from the data (the predicate names and logic), which is a core principle of good software design.
The original UnifyWeaver system used a simple render_template/3 predicate that took a string as input. The modern system revolves around named templates.
Instead of passing a long string to a predicate, you refer to a template by its name (an atom like bash_header or function_definition). The system then finds and renders that template according to a configurable strategy.
The main predicate you will use is render_named_template/3:
render_named_template(+TemplateName, +Dict, -Result)
The most powerful feature of the new template system is its ability to load templates from different sources. The system is governed by the source_order option, which is a list that tells UnifyWeaver where to look for a template and in what order.
The three possible sources are:
file: Looks for a template file in the templates/ directory (e.g., templates/my_template.tmpl.sh). This is the most flexible option and is recommended for customization.cached: Looks for a template in an in-memory cache. The system can be configured to automatically cache templates after they are loaded from a file or generated.generated: Looks for a hardcoded template defined inside template_system.pl using the template/2 fact. This is the fallback and provides the default behavior.You can control the source order globally or for a specific template.
Example: Preferring file-based templates globally
By default, the system only looks for generated templates. You can change this by setting a new global default.
% In your Prolog session
?- use_module(unifyweaver(core/template_system)).
?- set_template_config_default([source_order([file, cached, generated])]).
Now, when you call render_named_template('my_template', Dict, Result), the system will:
templates/my_template.tmpl.sh.my_template in the in-memory cache.generated template(my_template, ...) fact inside template_system.pl.Let’s see how you can use this system to customize the generated code without touching the compiler itself.
UnifyWeaver has a built-in, generated template named bash_header.
Goal: We want to add a custom comment to the header of all our generated scripts.
At the start of your Prolog session, tell the system to prefer file-based templates.
?- set_template_config_default([source_order([file, generated])]).
Create a new file named templates/bash_header.tmpl.sh. The name is important: it must match the name of the template you want to override (bash_header) and be in the configured templates directory.
File content for templates/bash_header.tmpl.sh:
#!/bin/bash
# ----------------------------------------
# Custom header for My Project
# Generated on: $(date)
# Description:
# ----------------------------------------
Now, when any part of the UnifyWeaver compiler renders the bash_header template, it will use your custom file instead of the built-in one.
?- render_named_template(bash_header, [description='My Ancestor Script'], Code).
Resulting Code:
#!/bin/bash
# ----------------------------------------
# Custom header for My Project
# Generated on: $(date)
# Description: My Ancestor Script
# ----------------------------------------
You have successfully customized the output without modifying the compiler’s source code. This powerful feature allows for extensive customization and theming of the generated Bash scripts.
The template system also includes an in-memory and on-disk caching layer. When configured, it can automatically save generated templates to a templates/cache directory. This is useful for two reasons:
Caching can be enabled in the configuration:
% Enable caching and file-based templates
?- set_template_config_default([
source_order([file, cached, generated]),
auto_cache(true)
]).
The modern template system is a significant enhancement that gives you fine-grained control over the generated code.
Key Takeaways:
bash_header).source_order option controls where the system looks for templates.templates/ directory.```
| ← Previous: Chapter 7: Variable Scope and Process Substitution | 📖 Book 2: Bash Target | Next: Chapter 9: Advanced Recursion Patterns → |