One of the most powerful features of the Prolog target is its ability to gracefully handle failures and automatically try alternative approaches. This chapter explores the fallback mechanisms that make the Prolog target robust and production-ready.
Consider these real-world scenarios:
gplc isn’t installedWithout fallback, these would be hard failures. With fallback, UnifyWeaver can:
Scenario: Requested compilation fails
Implementation: compile_script_safe/3
compile_script_safe(Dialect, ScriptPath, Options) :-
catch(
compile_script(Dialect, ScriptPath),
error(compilation_failed(FailedDialect, ExitCode), Context),
( % Compilation failed - handle gracefully
format('[PrologTarget] WARNING: ~w compilation failed (exit ~w)~n',
[FailedDialect, ExitCode]),
% Check if we should fail or continue
( option(fail_on_compile_error(true), Options)
-> % Propagate error if strict mode
throw(error(compilation_failed(FailedDialect, ExitCode), Context))
; % Otherwise continue with interpreted script
format('[PrologTarget] Continuing with interpreted script: ~w~n', [ScriptPath]),
format('[PrologTarget] NOTE: Full multi-dialect fallback planned for v0.2~n')
)
)
).
Behavior:
gplcfail_on_compile_error(true)): Re-throw errorExample:
% Request compilation
write_prolog_script(Code, 'test.pl', [dialect(gnu), compile(true)]).
% If gplc fails:
% [PrologTarget] WARNING: gnu compilation failed (exit 1)
% [PrologTarget] Continuing with interpreted script: test.pl
% [PrologTarget] NOTE: Full multi-dialect fallback planned for v0.2
% Script still created and usable with: gprolog --consult-file test.pl
Scenario: Multiple dialects specified in preference order
Current Status: Planned for v0.2
Proposed API:
generate_prolog_script(Predicates,
[dialects([gnu, swi]), % Preference order
compile(true),
fallback(auto)],
Code)
Planned Behavior:
Implementation Sketch:
generate_with_fallback([Dialect|Rest], Predicates, Options, Code) :-
catch(
( % Try current dialect
generate_prolog_script_single(Predicates,
[dialect(Dialect)|Options],
Code),
% Validate if compilation requested
( option(compile(true), Options)
-> validate_compilation(Dialect, Code)
; true
)
),
Error,
( % Failed - try next dialect
format('[Fallback] ~w failed: ~w~n', [Dialect, Error]),
Rest \= []
-> generate_with_fallback(Rest, Predicates, Options, Code)
; % No more fallbacks
throw(Error)
)
).
Scenario: Security policy restricts dialect usage
Integration: Firewall policies can block certain dialects
% In firewall policy
:- disallow(prolog_target(gnu)).
:- disallow(compile(native)).
Behavior:
% User requests GNU Prolog
generate_prolog_script(Predicates, [dialect(gnu)], Code).
% Firewall blocks it:
% [Firewall] DENIED: prolog_target(gnu) - policy violation
% [Fallback] Trying alternative: swi
% Falls back to SWI-Prolog
Implementation:
select_dialect_with_policy(RequestedDialect, Options, SelectedDialect) :-
% Check firewall
( firewall_allows(prolog_target(RequestedDialect))
-> SelectedDialect = RequestedDialect
; % Blocked - find allowed alternative
format('[Firewall] DENIED: prolog_target(~w)~n', [RequestedDialect]),
option(fallback_dialects(Alternatives), Options, [swi]),
member(AltDialect, Alternatives),
firewall_allows(prolog_target(AltDialect)),
!,
format('[Fallback] Using allowed dialect: ~w~n', [AltDialect]),
SelectedDialect = AltDialect
).
Scenario: Code uses features not available in requested dialect
Example: Code uses SWI-specific features but requests GNU Prolog
% Code uses tabling (SWI-Prolog specific)
:- table fib/2.
fib(0, 0) :- !.
fib(1, 1) :- !.
fib(N, F) :- N > 1, N1 is N-1, N2 is N-2, fib(N1, F1), fib(N2, F2), F is F1+F2.
Validation:
validate_for_dialect(gnu, [fib/2], Issues).
% Issues = [unsupported_feature(tabling, 'GNU Prolog does not support tabling')]
Fallback:
generate_with_validation(Predicates, Options, Code) :-
option(dialect(RequestedDialect), Options, swi),
% Validate
validate_for_dialect(RequestedDialect, Predicates, Issues),
( Issues = []
-> % No issues - proceed
generate_prolog_script(Predicates, Options, Code)
; % Has issues - check fallback
format('[Validation] Issues with ~w: ~w~n', [RequestedDialect, Issues]),
( option(fallback(auto), Options)
-> % Try alternative
find_compatible_dialect(Predicates, CompatibleDialect),
format('[Fallback] Using compatible dialect: ~w~n', [CompatibleDialect]),
generate_prolog_script(Predicates,
[dialect(CompatibleDialect)|Options],
Code)
; % No fallback - fail with issues
throw(error(incompatible_dialect(RequestedDialect, Issues)))
)
).
When: Compilation fails Action: Continue with interpreted script, log warning Use case: Development, testing
Options = [dialect(gnu), compile(true)]
% If compilation fails, script is still usable interpreted
When: Any failure Action: Throw error immediately Use case: CI/CD, production validation
Options = [dialect(gnu), compile(true), fail_on_compile_error(true)]
% Compilation failure → error thrown
When: Requested dialect fails Action: Try alternatives in order Use case: Maximum portability
Options = [dialects([gnu, swi]), fallback(auto), compile(try)]
% Try: GNU compiled → GNU interpreted → SWI interpreted
When: Target has limitations Action: Generate code with workarounds Use case: Feature-limited environments
Options = [dialect(gnu), fallback(best_effort)]
% Remove unsupported features, add comments explaining changes
User Request
│
├─ Check Firewall
│ ├─ Allowed → Continue
│ └─ Blocked → Try alternatives or fail
│
├─ Validate Compatibility
│ ├─ Compatible → Continue
│ └─ Incompatible → Fallback or fail
│
├─ Generate Code
│ ├─ Success → Continue
│ └─ Failure → Fallback or fail
│
└─ Compile (if requested)
├─ Success → Done ✓
├─ Failure + Silent → Use interpreted ✓
└─ Failure + Strict → Error ✗
You can implement custom fallback logic:
my_robust_generate(Predicates, PreferredDialect, Code) :-
% Strategy: Try preferred, then any compatible, then fail with diagnostics
catch(
% Attempt 1: Preferred dialect
generate_prolog_script(Predicates,
[dialect(PreferredDialect), compile(true)],
Code),
Error1,
( format('[Attempt 1] ~w failed: ~w~n', [PreferredDialect, Error1]),
% Attempt 2: Find compatible dialect
catch(
( find_compatible_dialect(Predicates, AltDialect),
AltDialect \= PreferredDialect,
format('[Attempt 2] Trying ~w~n', [AltDialect]),
generate_prolog_script(Predicates,
[dialect(AltDialect), compile(false)],
Code)
),
Error2,
( format('[Attempt 2] ~w failed: ~w~n', [AltDialect, Error2]),
% Attempt 3: SWI-Prolog interpreted (most compatible)
format('[Attempt 3] Falling back to SWI-Prolog interpreted~n'),
generate_prolog_script(Predicates,
[dialect(swi), compile(false)],
Code)
)
)
)
).
All fallback attempts are logged for debugging:
% Enable verbose logging
set_prolog_flag(verbose, normal).
% Generate with logging
generate_prolog_script([factorial/2],
[dialect(gnu), compile(true), verbose(true)],
Code).
% Output:
% [PrologTarget] Dialect: gnu
% [PrologTarget] Compilation requested: true
% [PrologTarget] Generating script...
% [PrologTarget] Compiling with gnu: gplc --no-top-level ...
% [PrologTarget] Compilation complete
dialects([gnu(compiled), gnu(interpreted), swi(interpreted)])
fallback(degrade) % Remove unsupported features, continue
dialect(auto) % Analyze code, pick best dialect
compile(cached) % Reuse successful compilations
on_fallback(log_and_notify) % Custom actions on fallback
[dialects([gnu, swi]), compile(true), fallback(auto)]
[fail_on_compile_error(true)]
% Test both paths
generate_prolog_script(Preds, [dialect(gnu)], GnuCode),
generate_prolog_script(Preds, [dialect(swi)], SwiCode).
catch(..., error(compilation_failed(D, E), C), ...)
format('[Fallback] Trying ~w after ~w failure~n', [Alt, Orig])
throw(error(all_dialects_failed(Attempts), Context))
robust_compile(Predicates, OutputPath) :-
% Try GNU compiled
catch(
( generate_prolog_script(Predicates,
[dialect(gnu), compile(true)],
Code1),
write_prolog_script(Code1, OutputPath,
[dialect(gnu), compile(true)])
),
_,
% Fall back to GNU interpreted
catch(
( generate_prolog_script(Predicates,
[dialect(gnu), compile(false)],
Code2),
write_prolog_script(Code2, OutputPath,
[dialect(gnu), compile(false)])
),
_,
% Fall back to SWI interpreted
( generate_prolog_script(Predicates,
[dialect(swi), compile(false)],
Code3),
write_prolog_script(Code3, OutputPath,
[dialect(swi), compile(false)])
)
)
).
Chapter 9 explores validation and compatibility checking in depth, showing how to detect issues before they cause fallback situations.
Key Takeaways:
| ← Previous: Chapter 7: The Firewall System | 📖 Book 11: Prolog Target | Next: Chapter 15: Case Study - Factorial Compilation → |