The Unrusting Blade: An Offline-First LogicBiz V.2.0 Powered by SWI-Prolog & SQLCipher - Reply 01

Thank you so much for sharing information about the LogicBiz V.2.0 project!

I use SWI-Prolog in my daily work, so it’s especially valuable to me to see this wonderful language being used to create serious commercial products. The very idea of ​​building the first offline POS system in Prolog sounds very bold and innovative.

Of course, without access to the source code, it’s difficult to fully understand the work done and the implementation details. However, even what you describe—the integration with SQLCipher, the performance, the architecture—inspires immense respect.

The most important thing I took away from your post is the joyful realization that I’m not alone. It’s gratifying to know that there are like-minded people who not only use Prolog in production but also achieve impressive results with it. This gives me strength and confidence that I’ve chosen the right path.

I wish you continued development of the project and continued success! It will be very interesting to follow the news.

Thank you so much for your kind words and encouragement! It genuinely means a lot to hear this from someone who uses SWI-Prolog in their daily production work.

To be completely honest, this project started out of pure passion and a lifelong fascination with predicate logic. Seeing the core engine successfully handle millions of rows with such high efficiency using declarative paradigms has been an incredibly rewarding journey.

While the source code is currently kept private as I focus on preparing the system for local small retail deployments, I am very glad that the architectural concepts and benchmarks resonated with you. Knowing that there are fellow developers out there pushing the boundaries of Prolog in the real world gives me great confidence as well.

Thank you again for the wonderful support. I wish you continued success in your daily work with SWI-Prolog!

(P.S. Please excuse any awkward phrasing, as I do not speak English and am using an AI assistant to translate my thoughts.)

Dear colleague,

Thank you so much for your honest and heartfelt reply! It makes me twice as happy to know that your project comes from pure passion—because that’s exactly where the best tools are born.

And what a coincidence! I also use Prolog in retail, though on a smaller scale. My own daily tool is a converter that collects sales data from fiscal cash registers (KSO) and transforms them into a unified format for upload into an ERP system. It’s not as grand as your engine, but it works reliably every single day—and that alone feels like a small victory for declarative logic in the real world.

Your story inspires me to think bigger. Knowing that someone else is quietly pushing Prolog forward in production—and in retail, no less—makes me feel less alone in this niche.

I completely understand your decision to keep the source private for now, especially while preparing for local deployments. When the time comes, I would be very curious to hear more about your architectural choices (if you ever feel like sharing non-confidential details).

And please don’t worry about your English—mine comes through a translator too, as I don’t speak it either. Perhaps that makes our conversation even more genuine :blush:

Wishing you strength and joy in bringing your system to life in small shops. Keep going—you’re doing something truly valuable.

With respect,
HeiHeShang

Dear HeiHeShang,

Thank you so much for your heartwarming and supportive reply! It is genuinely inspiring to connect with another developer who leverages Prolog for production systems in the retail sector.

To give you a clearer picture of my system’s architecture without breaking encapsulation, I ran a cross-reference analysis using Prolog XREF and generated a dependency graph.

The entire ecosystem is composed of roughly 19,000 lines of declarative code. As you can see from the breakdown below, “retail_brain.pl” acts as the central orchestration engine that binds all peripheral modules together:

  • DATABASE.PL: 1,181 lines
  • RETAIL_AUDIT.PL: 1,078 lines
  • RETAIL_BRAIN.PL: 5,929 lines
  • RETAIL_DISPLAY.PL: 497 lines
  • RETAIL_FUNCTIONAL_AUDIT.PL: 255 lines
  • RETAIL_MANIFESTO.PL: 464 lines
  • RETAIL_POS.PL: 1,154 lines
  • RETAIL_PRINT.PL: 793 lines
  • RETAIL_SECURITY.PL: 460 lines
  • RETAIL_SERVER.PL: 6,217 lines

Total Codebase: ~19,028 lines

Even though my predicate names and internal structures are written in Indonesian, keeping a strict separation of concerns across these modules makes the application highly predictable and solid for daily retail operations.

I have just written and uploaded a comprehensive breakdown of the core engine predicates and module interfaces in my repository. You can read the full documentation directly here:

I would love to hear your thoughts on this modular layout or how you handle dependency separation in your own Prolog tools!

Best respects,

Your Name / lokinpendawa

Thank you for your openness!

I have to be honest — when I hear about a single module with 6000 lines, my first thought is: this is a strong candidate for refactoring. Not because it’s “wrong”, but because maintainability suffers. Reading, reasoning about side effects, debugging, and even testing become exponentially harder beyond ~1000 lines, regardless of whether the code is written by a human or generated by an AI.

I say this with respect, because I know you are building something complex. But in my retail Prolog projects, I’ve learned that small, focused modules with clear interfaces save far more time than they cost to create. AI can help with the refactoring too, by the way — it’s not an obstacle, it’s a tool.

If performance is the concern, I’d be genuinely curious to hear how you handle that with such large units. Maybe I’m missing something.

In any case, this is just my two cents. I’m glad we can have this kind of discussion across languages and tools.

Thank you for your valuable feedback! To be honest, I am quite flattered. I am a complete beginner and have only been learning coding from scratch for about 2 months now, so hearing this from the community means a lot to me.

Regarding the refactoring, you are absolutely right. I actually planned to split the module when it hit around 3,000 lines. However, as a beginner, I was deeply afraid of breaking the interconnected logic and side-effects across dependencies, so I kept putting it off, and now it has snowballed into 6,000 lines! I definitely plan to refactor it carefully using modules moving forward.

As for the performance and how I managed the execution speed without hitting memory exhaustion (OOM), my approach was purely empirical. For every block of logic or page I built, I immediately stress-tested it by injecting millions of dummy data rows into the RAM.

I literally benchmarked different iteration mechanisms and higher-order predicates against each other to see how they behave with millions of tracking points. Specifically, I compared:

  1. findall/3, bagof/3, and setof/3 for data accumulation.
  2. aggregate_all/3 for memory-optimized statistical calculations.
  3. forall/2 and failure-driven loops (fail) to achieve a zero-memory footprint by forcing immediate internal backtracking and clearing the stacks.
  4. Manual recursion combined with strict Tail-Call Optimization (TCO) and proper cuts (!) to ensure the virtual machine reuses stack frames instead of bloating the global stack.

Whichever implementation clocked the fastest runtime and cleanest memory footprint under heavy stress was the one I kept. In this specific cryptography verification module (SHA-256 & AES-256), stripping away choicepoints and ensuring strict determinism was the key to handling 2,000,000 records smoothly under a tight hardware constraint (8GB RAM shared with GPU).

I am glad this sparked an interesting technical discussion across tools and languages!

To give you a concrete example, here is a snippet of how I structured the verification loop to ensure strict determinism and zero-memory bloat during the real-time console print:

cek_integritas_nota_manual(IDTx) :-
    % 1. Fetch data sequentially from in-memory facts
    riwayat_tx(IDTx, _Barcode, _Qty, LabaNet, Th, Bl, Tgl),
    
    % 2. Execute the SHA-256 cryptographic verification
    buat_hash_keamanan_nota(IDTx, Th, Bl, Tgl, LabaNet, HashBaru),
    
    % 3. Print verified status directly to console
    format('[INTEGRITY OK] Tx: ~w | SHA-256 Verified: ~w~n', [IDTx, HashBaru]), 
    !. % Strict cut to eliminate choicepoints immediately

print_verifikasi_hash_nota(Limit) :-
    % 1. Extract only the IDs into a list to minimize memory footprint
    findall(ID, retail_brain:riwayat_tx(ID, _, _, _, _, _, _), SemuaID),
    (   length(SemuaID, TotalDatabase), TotalDatabase > Limit
    ->  length(SampelID, Limit), append(SampelID, _, SemuaID)
    ;   SampelID = SemuaID
    ),
    length(SampelID, TotalAktual),
    
    get_time(WaktuMulai),
    nl, writeln('=== STARTING STEP-BY-STEP INTEGRITY PRINT ==='),
    
    % 2. Leverage forall/2 for a failure-driven loop to clear stacks on every iteration
    forall(member(IDTx, SampelID), (
        retail_brain:cek_integritas_nota_manual(IDTx)
    )),
    
    writeln('=== END OF STEP-BY-STEP INTEGRITY PRINT ==='),
    get_time(WaktuSelesai),
    WaktuProses is WaktuSelesai - WaktuMulai,
    
    nl,
    writeln('-----------------------------------------------------------------'),
    format('  MEASUREMENT TARGET    : SHA-256 Data Integrity Verification~n', []),
    format('  TOTAL INVOICES HASHED : ~w Invoices~n', [TotalAktual]),
    format('  EXECUTION TIME        : ~4f seconds~n', [WaktuProses]),
    writeln('-----------------------------------------------------------------'), nl, !.

By isolating the list to just IDs, cutting choicepoints at the leaf predicate, and utilizing `forall/2` for internal backtracking, the SWI-Prolog virtual machine can keep recycling the same stack frames efficiently even when scaled up to millions of records. I used that code to test the retrieval of 2,000,000 records.

Thank you for sharing such a detailed and impressive technical breakdown! Your empirical approach to benchmarking findall/3 vs forall/2 vs manual recursion with TCO is genuinely instructive — and handling 2,000,000 records on 8GB RAM shared with a GPU is no small feat. I take my hat off to you.

That said, I must admit that your post made me smile a little, because it highlights a fascinating gap between benchmark-scale retail and real-world retail.

In an actual store, a typical transaction rarely exceeds 10–20 line items. Most customers buy just a few products. So while your SHA-256 integrity verification over 2 million invoices is technically brilliant, in day-to-day operations we rarely — if ever — need to process that many records at once. The real challenges lie elsewhere.

What truly keeps me awake at night is not big data, but hardware integration.

In my country (and I suspect in yours too), a retail system cannot exist in isolation. We have to connect to:

  • Barcode scanners (USB, RS-232, or Bluetooth) — which are relatively easy,

  • Fiscal cash registers / KSO (online fiscal printers) — for legally compliant receipts,

  • State traceability systems — for tracking certain goods (e.g., tobacco, alcohol, or pharmaceuticals),

  • Payment terminals — for card and QR payments,

  • Weight scales — for fresh produce,

  • ERP and inventory systems — for stock management.

And all of this must happen in real time, with sub-second response times, because customers are waiting at the checkout.

So my genuine question to you — and I ask this with great curiosity, not as a challenge — is:

How do you interface all this physical and governmental infrastructure from Prolog?

Do you use C/C++ extensions? FFI calls to system libraries? A separate middleware layer (e.g., Node.js or Python) that acts as a bridge between Prolog logic and hardware drivers? Or do you use SWI-Prolog’s own foreign language interface (foreign.pl) to talk directly to USB/HID devices and fiscal printers?

Also, how do you handle the state traceability requirements? In many countries, every sale of certain goods must be reported to a government API in real time, with digital signatures and timestamps. Does your system do that directly, or do you offload that to an external service?

I am asking because this is where I struggle the most. My Prolog converter aggregates sales from KSO and transforms them for ERP, but the live connection to fiscal hardware and government APIs is something I currently handle outside Prolog — and I’m genuinely curious if you found a way to bring more of that into Prolog itself.

Please don’t take this as criticism of your work — it’s actually the opposite. Your scale achievements are remarkable. But I’m even more interested in how you solve the messy, physical, bureaucratic side of retail. That’s the real battle, isn’t it?

Looking forward to your thoughts!

Thank you for the detailed breakdown! One thing caught my attention — I didn’t see any tests in your modules. With 6,000 lines of cryptographic logic handling financial data, how do you test it?

Do you use SWI-Prolog’s plunit? Manual testing with trace/0? Property-based testing? Or do you have an external test harness?

I’m asking because in my experience, even 300-line modules become brittle without tests. And for legally binding receipts, I’d be nervous deploying without a solid test suite. Curious about your approach!

Thank you for the fascinating perspective! You hit the nail on the head regarding the “real-world battle” of retail hardware and bureaucracy.

To be completely honest, as a beginner who has only been coding from scratch for about 2 months, I haven’t implemented full integrations for all governmental APIs or heavy fiscal printers yet. However, the system architecture is already built to handle them seamlessly.

The “secret” to how LogicBiz bridges the gap between Prolog and the physical/bureaucratic world is that it runs as a Hybrid Web Server via local host (localhost:8811 or 127.0.0.1). To be precise, all of this web interface routing and frontend-to-backend data bridging is comfortably isolated inside a single file named retail_pos.pl.

By offloading the messy hardware side to the frontend web interface, things become remarkably simple:

  1. Barcode Scanners & EDC inputs: Barcode scanners inherently act as Human Interface Devices (HID) — meaning they just inject keyboard strokes into the active field. Since my input terminal is already standard web HTML, a physical USB/Bluetooth scanner works instantly out-of-the-box without needing complex C/C++ bindings or custom drivers in Prolog. The same applies to manually entering EDC trace codes.
  2. APIs, Printing & Middleware: Since retail_pos.pl acts as the core HTTP/JSON server here, any upcoming real-time state traceability requests can easily be handled using Prolog’s built-in library(http/http_client). For printing receipts, I don’t even need custom printer drivers; I simply let the browser handle the hardware communication (e.g., via standard print triggers straight to an EPSON L120).

The best part? This entire hybrid architecture runs 100% offline. The data flows instantly within the internal RAM circuit through the loopback address, meaning it requires zero internet reliance and guarantees zero network latency.

Lastly, regarding the 2,000,000 records stress test — it was intentionally designed to prove a point: to see whether Prolog is capable of handling enterprise-scale workloads for big corporations. And the results speak for themselves!

==========================================

You raise a very critical point regarding testing and software reliability, especially when dealing with legally binding financial data!

To be completely honest, since I am only 2 months into my coding journey, I haven’t adopted structured frameworks like plunit yet. Instead, my testing methodology is purely empirical, incremental, and highly rigorous through manual verification combined with heavy stress-testing.

Here is exactly how I verified the 6,000-line module to ensure absolute accuracy before scaling up:

  1. Incremental Financial Verification: I started testing with exactly 1 transaction, then moved to 2, 10, 100, and 1,000 transactions. During this phase, I strictly monitored the integrity of stock control, selling prices, and buying prices (COGS/HPP). I made sure that gross profit, 11% VAT (PPN), and the multi-layered discount logic—which combines both the VIP Member Discount and the CLP(R) AI Bundling Discount simultaneously—are mathematically flawless and match down to the exact Rupiah.
  2. Stress-Testing for Scale: Once these regression tests proved that the financial logic was 100% airtight and accurate on a small scale, only then did I inject the 2,000,000 dummy records. This final stage was strictly a performance stress-test to measure Prolog’s ability to cover massive enterprise-scale workloads for big corporations.

So, while it’s wrapped in a single module for now, every sub-predicate tree has been heavily audited through gradual data injection to ensure the calculation never breaks!