-
Notifications
You must be signed in to change notification settings - Fork 24
Strange Prolog Coder Hacks
Prolog programmers often unconventional techniques to handle scenarios like undefined predicates, conditional execution, or prototyping code. This page documents some of these seemingly nonsensical things you'll see in the mettalog codebase.
nop(_).
do_stuff:-
nop(step1),
step2.The nop/1 predicate always succeeds without doing anything, regardless of the argument passed to it. This trick is useful for temporarily disabling parts of code while maintaining readability and structure.
-
Placeholders for Disabled or Future Code: Use
nop/1as a quick way to "comment out" code without removing it. - Debugging and Prototyping: Selectively deactivate steps in a process without breaking the flow of the program.
- Readable and Modular Code: Even when a step is inactive, it remains visible in the code, preserving the overall structure.
do_something:-
call(call, some_pred(stuff)).In SWI-Prolog, when you use call(some_pred(stuff)) and some_pred/1 is not yet defined, you’ll receive a warning like "Procedure some_pred/1 does not exist." However, by wrapping the call in an extra call, like call(call, some_pred(stuff)), you can reference a predicate that hasn’t been defined yet without triggering this warning.
- Deferring Definition: This trick is handy when you want to reference a predicate that you’ll define later. It allows you to build the structure of your code without needing every part implemented upfront.
- Modular Development: You can design complex systems where certain predicates are loaded or defined dynamically, while still referencing them early in the code.
-
Avoiding Compilation Warnings: SWI-Prolog is smart enough to issue warnings about undefined predicates when you use
call(some_pred(stuff)). Usingcall(call, some_pred(stuff))suppresses these warnings until the predicate is defined.
- The first
calleffectively delays the evaluation of the innercall(some_pred(stuff)), allowing Prolog to treat it as a more dynamic or indirect reference. - Once
some_pred/1is eventually defined, the code will run as expected.
- For scenarios where predicates are loaded dynamically, such as in modular or plugin-based systems.
- When you’re building code that evolves over time, and you want to define certain predicates later, use
nop/1instead.
Here’s an expanded section for the Strange_Prolog_Coder_Hacks wiki page that includes using fail/0 to effectively comment out an entire clause:
do_something :-
step1,
step2,
step3.
do_something_else :-
fail, % This clause will always fail, effectively commenting it out
stepA,
stepB,
stepC.The fail/0 predicate is built into Prolog and, as the name suggests, it always fails. By placing fail/0 as the first goal in a clause, the entire clause will immediately fail when executed, preventing any further goals in that clause from being evaluated.
-
Temporarily Disable a Clause: During development, you might want to disable a specific clause without removing it from the code. Using
fail/0is a quick and effective way to "comment out" the entire clause while keeping it in place. -
Preserve the Structure for Future Use: Unlike traditional commenting (using
%for line comments), usingfail/0allows you to keep the clause intact and visible within your code. This is useful when you plan to re-enable or modify the clause later. -
Maintain Control Over Clauses Without Deleting Them: By using
fail/0, you can easily revert back to the original functionality by simply removing or commenting out thefail/0line.
When Prolog encounters fail/0 in a clause, it stops evaluating the remaining goals in that clause and immediately considers the clause as having failed. If there are other clauses for the same predicate, Prolog will attempt to match those instead. If there are no other clauses, Prolog will return false for that predicate.
-
Debugging and Testing: If you’re testing a different implementation or alternative logic, you can temporarily disable the original clause using
fail/0. -
Prototyping: When you’re unsure about the behavior of a specific clause or want to delay its execution until later, using
fail/0allows you to keep it in the codebase without it being active. -
Conditional Logic: In some cases, you may want to programmatically control whether a clause should be active or not. By conditionally including or excluding
fail/0, you can switch between enabling and disabling a clause.
- Be cautious when using this technique, as leaving
fail/0in place for too long can lead to forgotten or outdated code being left inactive in your project. - It’s easy to forget why a clause was disabled with
fail/0, so consider adding a comment explaining the reason if it’s a temporary measure. - While
fail/0is an effective tool, if you simply want to disable a single goal rather than the entire clause, traditional line comments using%might be more appropriate.
Here’s an addition to the Strange_Prolog_Coder_Hacks wiki page that explains using a cut (!) in the first clause to temporarily comment out all clauses below it:
Prolog provides a number of powerful tools that, when used creatively, can lead to clever solutions for common development challenges. One such hack involves using a cut (!) at the beginning of a clause to temporarily prevent other clauses from being considered.
do_something :-
!, % Cut prevents any further clauses from being considered
step1,
step2.
do_something :-
stepA,
stepB.
do_something :-
stepX,
stepY.In Prolog, the cut (!) is used to commit to a specific choice, preventing Prolog from backtracking and considering alternative clauses. By placing a cut at the beginning of the first clause, you effectively disable all the clauses below it, making Prolog commit to the first clause regardless of whether it succeeds or fails.
-
Temporarily Disable Lower Clauses: If you’re still developing or refining the logic in the clauses below, you can use this technique to prevent Prolog from even considering them until they’re ready.
-
Controlled Testing of a Single Clause: During development, you may want to test just one specific implementation of a predicate. By placing a cut in the first clause, you ensure that only this clause is executed, while the others are temporarily ignored.
-
Postpone Decisions on Deleting Clauses: If you’re unsure whether you want to delete certain clauses, using a cut allows you to effectively "comment them out" without losing the code. You can later decide whether to keep or remove them based on further testing.
- The cut (
!) in Prolog tells the interpreter, "Commit to this choice and don’t backtrack." When placed at the start of a clause, it guarantees that if this clause is reached, no further clauses will be evaluated, even if this clause fails. - As a result, all clauses below the one with the cut are effectively ignored.
- Incremental Development: If you’re working on new logic and want to focus on testing one particular approach, you can temporarily disable the other clauses using a cut.
- Selective Clause Activation: When you have multiple versions of a predicate, you can use a cut to activate only one version while leaving the others in place for future comparison.
- Deferred Cleanup: Before permanently deleting old or deprecated clauses, you can disable them with a cut while ensuring the code remains functional.
- Unintended Behavior: The cut completely blocks backtracking, so if the first clause fails, Prolog won’t attempt any of the remaining clauses. This can lead to unexpected failures if you forget the cut is in place.
- Hidden Logic: The clauses below the cut are still in your code, which can be confusing for others (or even yourself) if it’s not clear why they aren’t being executed.
- Clearly document the use of this technique, especially if it’s temporary. Add comments explaining why the cut is used and when the blocked clauses should be reactivated or removed.
- If the cut is being used for debugging, consider removing it once the debugging phase is complete to avoid leaving inactive code in the project.
- Using a cut (
!) at the start of a clause is a clever way to "comment out" all lower clauses temporarily. It’s a practical technique for incremental development, controlled testing, and delaying the decision to delete code until you’re sure it’s no longer needed.