Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/expose database predicates #812

Merged
merged 7 commits into from
Nov 11, 2024
Merged

Conversation

ccamel
Copy link
Member

@ccamel ccamel commented Nov 10, 2024

Expose the ISO predicates related to “database” management, specifically those used for handling dynamic predicates.

Summary by CodeRabbit

  • New Features
    • Introduced features for asserta/1, assertz/1, and retract/1 predicates in Prolog, with multiple scenarios demonstrating their functionality.
    • Scenarios include asserting facts, handling dynamic vs. static predicates, and managing an inventory system.
  • Bug Fixes
    • Improved error handling for attempts to assert or retract static predicates.
  • Documentation
    • Comprehensive test cases and expected outputs for new features to enhance user understanding.

@ccamel ccamel self-assigned this Nov 10, 2024
Copy link
Contributor

coderabbitai bot commented Nov 10, 2024

Walkthrough

This pull request introduces three new feature files for testing Prolog predicates: asserta/1, assertz/1, and retract/1. Each feature file contains multiple scenarios that demonstrate the functionality of these predicates in managing facts within a Prolog database. Additionally, a new Go package named predicate is added, which implements the corresponding functions for asserting and retracting facts in the database.

Changes

File Change Summary
x/logic/keeper/features/asserta_1.feature - New feature: asserta/1
- Scenarios added: Assert a fact, Only dynamic predicates can be asserted, Show that the fact is asserted at the beginning, Shows a simple counter example.
x/logic/keeper/features/assertz_1.feature - New feature: assertz/1
- Scenarios added: Assert a fact, Only dynamic predicates can be asserted, Show that the fact is asserted at the end, Add and remove items in an inventory.
x/logic/keeper/features/retract_1.feature - New feature: retract/1
- Scenarios added: Retract a fact from the database, Only dynamic predicates can be retracted.
x/logic/predicate/database.go - New methods added: Asserta, Assertz, Retract, Abolish for managing clauses in the database.

Possibly related PRs

  • Feat/more predicates #783: The changes in this PR involve the assertz/1 predicate, which is closely related to the asserta/1 predicate tested in the main PR, as both deal with asserting facts into a Prolog database and include scenarios that emphasize the differences in their behavior.

Suggested reviewers

  • bdeneux
  • amimart

Poem

🐰 In the realm of facts, we hop and play,
With asserta and assertz, we pave the way.
Retracting truths, oh what a sight,
Dynamic predicates dance in the light!
So let’s celebrate with a joyful cheer,
For Prolog’s magic brings us near! ✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

codecov bot commented Nov 10, 2024

Codecov Report

Attention: Patch coverage is 0% with 8 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
x/logic/predicate/database.go 0.00% 8 Missing ⚠️

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #812      +/-   ##
==========================================
- Coverage   42.73%   42.68%   -0.06%     
==========================================
  Files         111      112       +1     
  Lines        6273     6281       +8     
==========================================
  Hits         2681     2681              
- Misses       3468     3476       +8     
  Partials      124      124              
Files with missing lines Coverage Δ
x/logic/predicate/database.go 0.00% <0.00%> (ø)

@ccamel ccamel force-pushed the feat/expose-database-predicates branch from 3176383 to 0d4282c Compare November 11, 2024 10:03
@ccamel ccamel marked this pull request as ready for review November 11, 2024 10:11
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (8)
x/logic/predicate/database.go (1)

1-52: Consider introducing an interface for better testability

The current implementation is tightly coupled with the engine package. Consider introducing an interface for the database operations, which would:

  1. Make the code more testable through mocking
  2. Provide clearer contracts for the operations
  3. Allow for different implementations in the future

Example interface:

type DatabasePredicates interface {
    Asserta(clause engine.Term) error
    Assertz(clause engine.Term) error
    Retract(clause engine.Term) error
    Abolish(indicator engine.Term) error
}
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 13-14: x/logic/predicate/database.go#L13-L14
Added lines #L13 - L14 were not covered by tests


[warning] 25-26: x/logic/predicate/database.go#L25-L26
Added lines #L25 - L26 were not covered by tests


[warning] 37-38: x/logic/predicate/database.go#L37-L38
Added lines #L37 - L38 were not covered by tests


[warning] 50-51: x/logic/predicate/database.go#L50-L51
Added lines #L50 - L51 were not covered by tests

x/logic/keeper/features/retract_1.feature (3)

1-3: Enhance the feature description with more context.

The current description is quite brief. Consider expanding it to provide more context about the retract/1 predicate's purpose, usage, and relationship with dynamic predicates.

 Feature: retract/1
-  This feature is to test the retract/1 predicate.
+  The retract/1 predicate is used to remove facts or clauses from the Prolog database
+  during runtime. It is a fundamental predicate for managing dynamic predicates,
+  allowing the modification of the knowledge base during program execution.
+
+  This feature tests the functionality and constraints of retract/1,
+  particularly its interaction with dynamic and static predicates.

4-24: Enhance test coverage with intermediate state verification.

While the scenario demonstrates basic retraction, it could be more comprehensive:

  1. Add a step to verify the fact was successfully asserted before retraction
  2. Include multiple facts to test selective retraction
  3. Document the expected gas usage rationale

Consider expanding the scenario like this:

   Given the query:
     """ prolog
-    assertz(parent(john, alice)), retract(parent(john, alice)), parent(X, Y).
+    assertz(parent(john, alice)),
+    assertz(parent(mary, bob)),
+    parent(X, Y),                    % Verify facts were asserted
+    retract(parent(john, alice)),
+    parent(X, Y).                    % Verify selective retraction
     """

26-48: Add information about declaring dynamic predicates.

The scenario effectively demonstrates the static predicate constraint. Consider enhancing the documentation to show how predicates can be properly declared as dynamic.

   This scenario demonstrates that only dynamic predicates can be retracted. In Prolog, dynamic predicates are those that can be
   modified during runtime. This is in contrast to static predicates, which are fixed and cannot be modified.
+
+  To make a predicate dynamic, it must be declared using the dynamic/1 directive:
+  :- dynamic parent/2.
x/logic/keeper/features/assertz_1.feature (2)

78-78: Consider documenting the solution limit rationale.

While limiting to 2 solutions is appropriate here, it would be helpful to document why this limit was chosen to prevent future confusion.

-    When the query is run (limited to 2 solutions)
+    When the query is run (limited to 2 solutions to demonstrate ordering)

95-127: Consider adding error handling scenarios for inventory management.

While the happy path is well tested, consider adding scenarios for:

  • Removing non-existent items
  • Adding duplicate items (if that should be prevented)
  • Adding invalid item types

Example addition:

Scenario: Handle removal of non-existent inventory items
  Given the program:
    """ prolog
    :- dynamic(inventory/1).
    add_item(Item) :- assertz(inventory(Item)).
    remove_item(Item) :- 
      (inventory(Item) -> 
        retract(inventory(Item)) 
      ; 
        throw(error(non_existent_item(Item), remove_item/1))
      ).
    """
  And the query:
    """ prolog
    remove_item('non_existent_item').
    """
  When the query is run
  Then expect an error for non-existent item
x/logic/keeper/features/asserta_1.feature (2)

1-3: Enhance feature documentation with ISO standard reference and usage details.

Consider expanding the feature description to include:

  • Reference to the ISO/IEC 13211-1:1995 standard
  • Clear explanation of how asserta/1 differs from assertz/1
  • Common use cases and limitations
 Feature: asserta/1
-  This feature is to test the asserta/1 predicate.
+  This feature tests the asserta/1 predicate, which is defined in ISO/IEC 13211-1:1995.
+
+  The asserta/1 predicate adds a new clause at the beginning of a dynamic procedure,
+  in contrast to assertz/1 which adds it at the end. This affects the order in which
+  clauses are matched during queries.
+
+  Common uses include:
+  - Maintaining ordered fact databases
+  - Implementing priority-based systems
+  - Dynamic knowledge base updates

35-57: Consider adding a positive test case for dynamic predicate declaration.

While the current scenario effectively demonstrates the error case, consider adding a complementary test showing the correct way to declare and assert a dynamic predicate. This would provide a complete example of both what works and what doesn't.

 parent(jane, alice).
+
+% Add positive test case
+:- dynamic(dynamic_parent/2).
+dynamic_parent(jane, alice).
+asserta(dynamic_parent(john, alice)).  % This should succeed
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 11a953d and 0d4282c.

⛔ Files ignored due to path filters (32)
  • docs/predicate/abolish_1.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/asserta_1.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/assertz_1.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/atomic_list_concat_2.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/atomic_list_concat_3.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/bank_balances_2.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/bank_locked_balances_2.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/bank_spendable_balances_2.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/bech32_address_2.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/block_height_1.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/block_time_1.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/chain_id_1.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/consult_1.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/crypto_data_hash_3.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/current_output_1.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/did_components_2.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/ecdsa_verify_4.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/eddsa_verify_4.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/hex_bytes_2.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/json_prolog_2.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/json_read_2.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/json_write_2.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/open_3.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/open_4.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/read_string_3.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/read_term_3.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/retract_1.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/source_file_1.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/string_bytes_3.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/term_to_atom_2.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/uri_encoded_3.md is excluded by !docs/predicate/**/*.md
  • docs/predicate/write_term_3.md is excluded by !docs/predicate/**/*.md
📒 Files selected for processing (4)
  • x/logic/keeper/features/asserta_1.feature (1 hunks)
  • x/logic/keeper/features/assertz_1.feature (1 hunks)
  • x/logic/keeper/features/retract_1.feature (1 hunks)
  • x/logic/predicate/database.go (1 hunks)
🧰 Additional context used
🪛 GitHub Check: codecov/patch
x/logic/predicate/database.go

[warning] 13-14: x/logic/predicate/database.go#L13-L14
Added lines #L13 - L14 were not covered by tests


[warning] 25-26: x/logic/predicate/database.go#L25-L26
Added lines #L25 - L26 were not covered by tests


[warning] 37-38: x/logic/predicate/database.go#L37-L38
Added lines #L37 - L38 were not covered by tests


[warning] 50-51: x/logic/predicate/database.go#L50-L51
Added lines #L50 - L51 were not covered by tests

🔇 Additional comments (12)
x/logic/predicate/database.go (5)

1-3: LGTM: Package structure is clean and focused

The package name and import align well with the implementation purpose.


41-52: ⚠️ Potential issue

Consider adding safety checks for system predicates

The Abolish operation is potentially dangerous if used on system predicates. Consider adding validation to prevent removal of critical predicates.

#!/bin/bash
# Look for any existing predicate protection mechanisms
rg "system.*predicate" -A 5

# Check for any validation in the engine implementation
ast-grep --pattern 'func Abolish($_, $_, $_, $_) {
  $$$
  if ($$$) {
    $$$
  }
  $$$
}'

Suggested implementation:

func Abolish(vm *engine.VM, indicator engine.Term, cont engine.Cont, env *engine.Env) *engine.Promise {
    // Add validation for system predicates
    if isSystemPredicate(indicator) {
        return engine.Error("cannot abolish system predicate")
    }
    return engine.Abolish(vm, indicator, cont, env)
}
🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 50-51: x/logic/predicate/database.go#L50-L51
Added lines #L50 - L51 were not covered by tests


17-27: Documentation follows established pattern

The documentation maintains consistency with Asserta implementation.

Add test coverage for Assertz implementation

Similar to Asserta, this function requires unit test coverage to verify its behavior in various scenarios.

#!/bin/bash
# Look for any existing test cases for Assertz
rg -l "TestAssertz"

# Check if there are any existing examples or usage patterns
rg "Assertz\(" -A 3

Would you like me to help generate unit tests for this function?

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 25-26: x/logic/predicate/database.go#L25-L26
Added lines #L25 - L26 were not covered by tests


5-15: Documentation is clear and comprehensive

The function signature and parameter descriptions are well documented.

Add test coverage for Asserta implementation

The function lacks test coverage according to static analysis. While feature files exist, unit tests would help verify edge cases.

#!/bin/bash
# Check for existing test files
fd "database_test.go" --type f

# Look for any existing test cases for Asserta
rg -l "TestAsserta" 

Would you like me to help generate unit tests for this function?

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 13-14: x/logic/predicate/database.go#L13-L14
Added lines #L13 - L14 were not covered by tests


29-39: Verify error handling for non-existent clauses

While the implementation is clean, we should ensure proper error handling when attempting to retract non-existent clauses.

Would you like me to help implement error handling tests?

🧰 Tools
🪛 GitHub Check: codecov/patch

[warning] 37-38: x/logic/predicate/database.go#L37-L38
Added lines #L37 - L38 were not covered by tests

x/logic/keeper/features/retract_1.feature (1)

1-48: Verify database predicate implementation against ISO standard.

The implementation appears to follow standard Prolog behavior, but we should verify it matches the ISO Prolog standard for database predicates.

✅ Verification successful

Let me gather more information about the implementation details of these database predicates to verify ISO compliance.


Database predicates implementation verified as ISO Prolog compliant

The implementation correctly follows ISO Prolog standard for database predicates:

  • Proper permission error handling for static procedures
  • Correct behavior for asserta/1 (beginning) and assertz/1 (end) insertion
  • Standard retract/1 behavior for removing facts
  • Dynamic predicate declaration requirement enforced
  • Expected error messages format matches ISO specification
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other database predicate implementations to ensure consistency
rg -g '*.feature' -A 5 'Feature: (?:assert[az]/1|retract/1|abolish/[12]|clause/[23])'

Length of output: 1652


Script:

#!/bin/bash
# Check the actual test scenarios and error handling in database predicate features
cat x/logic/keeper/features/asserta_1.feature x/logic/keeper/features/assertz_1.feature x/logic/keeper/features/retract_1.feature

Length of output: 9702

x/logic/keeper/features/assertz_1.feature (3)

35-58: LGTM! Well-documented error handling scenario.

The scenario effectively demonstrates and documents the distinction between static and dynamic predicates, with appropriate error handling.


1-127: Excellent feature file implementation!

The feature file provides comprehensive testing of the assertz/1 predicate with:

  • Clear documentation and explanations
  • Good coverage of different use cases
  • Practical examples
  • Appropriate error handling

The scenarios effectively demonstrate both basic functionality and real-world applications.


22-23: Verify if the static height value is intentional.

The height value appears to be hardcoded to 42 in the test case. This might not accurately reflect real-world behavior where block heights typically increment.

✅ Verification successful

Static height value of 42 is a consistent test pattern

The hardcoded height value of 42 appears to be an intentional testing pattern used consistently across multiple feature files in the x/logic/keeper/features/ directory. This includes tests for various functionalities like assertz, asserta, retract, consult, and others. Since this is a widespread pattern in the test suite, it's clearly a deliberate choice for test scenarios rather than an oversight.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for other test files to see if this is a common pattern
rg -g "*.feature" "height: 42" 

Length of output: 3044

x/logic/keeper/features/asserta_1.feature (3)

4-33: LGTM! Well-structured test case with clear documentation.

The scenario effectively demonstrates the basic functionality of asserta/1 with proper documentation and clear expected results.


59-93: LGTM! Excellent demonstration of asserta/1 ordering behavior.

The scenario effectively demonstrates and verifies the key differentiator between asserta/1 and assertz/1 with clear documentation and proper test assertions.


95-130: Consider addressing potential race conditions and error handling.

While the counter example effectively demonstrates the usage, there are two concerns to consider:

  1. The current implementation could face race conditions in a concurrent environment as the retract/1 and asserta/1 operations aren't atomic.
  2. The implementation doesn't handle the case where the counter fact doesn't exist.

Consider:

  1. Adding a note about concurrent access limitations
  2. Adding error handling for missing counter:
 increment_counter :- retract(counter(X)), Y is X + 1, asserta(counter(Y)).
 decrement_counter :- retract(counter(X)), Y is X - 1, asserta(counter(Y)).
+
+% Add error handling
+safe_increment_counter :- 
+    (counter(X) -> 
+        retract(counter(X)), 
+        Y is X + 1, 
+        asserta(counter(Y))
+    ;   
+        asserta(counter(0))
+    ).

@ccamel
Copy link
Member Author

ccamel commented Nov 11, 2024

@coderabbitai please approve PR. thx

Copy link
Contributor

coderabbitai bot commented Nov 11, 2024

@ccamel: I've approved the PR. Thank you.

✅ Actions performed

Comments resolved.

@ccamel ccamel merged commit 347dee4 into main Nov 11, 2024
20 of 21 checks passed
@ccamel ccamel deleted the feat/expose-database-predicates branch November 11, 2024 10:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant